TextSecure

Updated: Tue, May 7, 2013 - 1:55pm

Let's talk about SMS for a minute: We all know that communication is completely insecure and that telcos routinely hand over all data to the government without even waiting for them to ask. So text messaging is compromised. What can we do?

A partial answer is to send encrypted text messages. The government still knows who you're messaging, and when, but they won't get the contents of the messages.

In order to send encrypted text messages, both you and the other party to this communication must have an app capable of doing encryption and decryption. Such an app is TextSecure, for android. It does text messaging using the OTR cryptosystem. It also saves your texts in an encrypted database on your phone.

Once you have this app, you can use it instead of the default "Messaging" app that comes with Android. It takes over all your texting duties, and you can use it to send encrypted texts to people who use this app, or non-encrypted texts to people who don't use this app. Due to the encryption overhead, you can only get 60 characters per encrypted text, instead of the, like, 140 or whatever for a plaintext text.

This app is open-source (of course, or I wouldn't be recommending it). You can, of course, download it for free at the google play store. If you want to do that, you don't need to read the rest of the post. Just get it and then start texting with me in an encrypted fashion.

However, if you are like me, you don't use the google play store because of privacy concerns. If you download apps from there, google knows that you've downloaded those apps. I don't like giving them that information. I don't even have the google play store installed on my phone. Instead, I use F-Droid, a separate app store that only distributes free, open-source apps, and lets you download them anonymously.

The problem is that the people who run F-Droid got into a little misunderstanding with the developers of TextSecure, so F-Droid doesn't distribute the app anymore. Briefly: There was a security bug. The TextSecure developers released the fix as a binary download on google play before making the fix available on their github repo. During this time, the developers of TextSecure found that f-droid was still distributing the out-of-date version (because they couldn't get the latest code), and asked them not to do that. The developers of TextSecure released their fix on github, but f-droid decided to take down TextSecure anyway. So now f-droid does not contain an encrypted SMS program.

So, your choices are: Give up your ability to download TextSecure anonymously, and get it from Google Play, or compile the app yourself. I chose the latter. I will now describe how to do that. Also, I will give you a third choice: Download the compiled, unsigned, app from here.

Here's how you compile the app:

  1. Make sure you have the Java Developer Kit
  2. Get the Android SDK
  3. Run the "Android SDK Manager":
    % android
    
  4. Install "Android 4.0.3 (API 15)". Important: Make sure you're using API 15. I have an old android phone, and I'm stuck with Android 2.3.3 (API 10), but you won't be able to compile the app unless you use API 15. Don't worry, the app compiled in this manner will still run on Android 2.3.3, due to the fact that its AndroidManifest.xml contains <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16"/>. Also, the special features from API15 can be emulated on earlier versions of Android by using ActionBarSherlock. More on that later.
  5. Once the API is installed, we must clone the git repos for TextSecure and for ActionBarSherlock.
    % git clone git://github.com/WhisperSystems/TextSecure.git
    % git clone git://github.com/JakeWharton/ActionBarSherlock.git
    
  6. TextSecure requires a particular version of ActionBarSherlock -- an older version -- version 4.2.0. So we need to make sure that's checked out.
    % cd ActionBarSherlock
    % git checkout -b 4.2.0 4.2.0
    % cd ..
    
  7. Both of the projects need to have the same version of the Android Support Library. Let's use the one from TextSecure.
    % cp TextSecure/libs/android-support-v4.jar ActionBarSherlock/library/libs/android-support-v4.jar
    
  8. Both projects need to be updated by the sdk. This is much like the "./configure" step in the gnu build system. Furthermore, TextSecure must be told to use ActionBarSherlock as a library project:
    % android update project --path ActionBarSherlock/library/ --target android-15 --subprojects
    % android update project --path TextSecure --target android-15 --library ActionBarSherlock/library/ --subprojects
    
  9. My JAVA_HOME variable is not set properly, so I had to set it:
    % export JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-amd64
    
  10. Finally, you can compile TextSecure:
    % cd TextSecure
    % ant debug
    
  11. You will now find bin/TextSecure-debug.apk. You can copy that to your phone and install it using a file browser, or you can copy it to a website and navigate to it to install it, or you can install it to your phone using the Android Debug Bridge which came with the SDK.
  12. Text me!
Your rating: None Average: 5 (107 votes)

P2P DNS

Updated: Wed, Jan 9, 2013 - 2:24pm
Concerning

I got an idea.

The Domain Name System has been the focus of interest from censors lately. The Pirate Bay and WikiLeaks have both had their domain names yanked out from under them, and this type of thing happens a lot to smaller sites as well. That's also been the proposed implementation of some of these anti-piracy bills.

So I started thinking about a P2P replacement for DNS, so that it is more difficult to shut down websites this way.

Here is my proposal: We have a distributed database that works like the Bitcoin block chain.

Here's the details.

First, we create a P2P network through the normal bootstraping algorithms. Now we have discovered peers.

If we want to publish a new domain name, we create a transaction. This contains:

  • A public key
  • A DNS record
  • A timestamp
  • A signature of the above

I figure the "DNS Record" would be in, like, bind zonefile format or something? I don't actually use bind, I've always used djbdns, but bind format is more widely supported. Maybe we can be agnostic and require support for bind and for djbdns format. Though that's annoying because it may make the software overly complex. Whatever, that's not the point.

The point is, you make this transaction and broadcast it to your peers, and you forward on transactions you hear about from other peers.

At some point, some peer decides to bundle the transactions into a block. A block is:

  • A public key
  • A list of transactions
  • A timestamp
  • The hash of the previous block
  • A hash of the abobe
  • A signature of the above

The hash of this block is constructed such that the first n bits of it must be 0. The number n is called the "difficulty" and can change over time as computers get better at generating hashes.

Since the block must include information about the previous block, this introduces the "block chain". You can query your peers about the block chain. If the chain has a branch in it -- that is, two blocks both claim block X as their parent -- you must believe the chain that is longer. If it's a tie, wait until someone has picked one arbitrarily and added to that chain. Now their chain is longer than the other one, so you should believe that one.

This is basically the bitcoin block chain.

Now, let's say someone wants to publish a change to a dns record. They must publish a new transaction that is signed with the same key as the original. Everybody must ignore transactions that are not signed properly.

Mostly, updates will be new zone files, but there will also be another type of transaction: Ownership change. That transaction looks like this:

  • Old owner's public key
  • New owner's public key
  • domain name
  • timestamp
  • signature from old owner

Then the new owner can go ahead and publish changes to the domain name.

Okay, so what can the bad guys do with this setup?

In order to insert bad-guy data, they'd have to control more than 50% of the computing power on the network, because otherwise, the good guys can publish new blocks faster than the bad guys can. Now, with bitcoin, there's an incentive for people to spend lots of money to buy a bunch of cpus -- that is, if you have a bunch of cpus, you get to claim the financial reward for solving the block. There is no such reward here. Also, the bad guys have a lot of money to spend. All the copyright people might pool their money and buy a big setup that could overpower the community one.

But what could they do?

One thing they could *not* do is take down domains. If they tried to insert a change to an existing domain, then they wouldn't be able to sign the transaction correctly, so everybody would reject this transaction. If they wrote this evil transaction, everybody would see that it was an illegal transaction and ignore it. If they wrote that transaction into a block, then the block would be ignored. Burrrnnnnn.

But what they *could* do is create a bunch of spam transactions and write them into blocks more quickly than anybody else could. In that way, they could make sure that nobody else could write transactions except for them. That would mean that nobody could change their dns info until this situation was resolved. They might want to do this if, for example, they shut down the pirate bay at one IP address, and wanted to make sure that the dns never got updated to point to their new address.

So, there's that. We'll cross that bridge when we come to it.

So who would run this program? Not normal clients. People would run the normal client if they wanted to insert transactions (and thus maintain their domain names). People would run this program if they wanted to run a dns server. But normal people would make normal dns queries to this dns server. The dns server would consult the blockchain backend, and return a normal dns result.

It is not even necessarily the case that the query would take longer than a normal dns query. It would take more disk space on the dns server, since it would have to keep the whole database online, and keep listening for updates.

Oh hey, look. After I wrote this up, I googled for it. It turns out that somebody is already doing pretty much exactly this.

Dot-bit. I got linked there from bitcoin's page on alternative blockchains.

Well, maybe I'll test out that software and tell you what is up.

Your rating: None Average: 3.7 (3 votes)

Firefox Sync Server

Type
Status

Today I set up a Firefox Sync server.

I use Firefox on my computer at work and at home. I wish that there was a way to synchronize my browsing history and tabs and whatnot.

Well, it turns out there is. If you go to the Tools menu, you can set up sync.

But I was like "Dude there's no way I'm going to do that. I don't want my private data living up on some server for anyone to see."

Well, it turns out that:
1) The data is encrypted before it's sent to the server, but more importantly:
2) You can set up your own server!

Here's their instructions on setting up a sync server. Note that you also have to set up a registration server.

They wrote them as separate parts, and I'm sure that they have it nicely partitioned into separate databases and stuff, but I just created one database for the sync server, and then added the one extra table from the registration server instructions.

They act like it's very hard to set up the sync server, and they "strongly recommend" setting up the minimal sync server. I respectfully disagree.

1. Their instructions for the minimal server will leave you with a configuration in which apache can write to a directory that code is executed out of.
2. The regular server is quite easy to set up if you have any familiarity with webapps.
3. The minimal server doesn't do user registrations. Instead, it gives you a script to add users. However, I couldn't figure out how to instruct firefox to log in when I'd created my account with this script. I needed a "sync key" and I don't believe I was given one. By contrast, with the regular server, I created my account through the browser's interface and it totally gave me a sync key.

So, now I've got my very own sync server. I can sync my firefox with peace of mind.

And if you want to use my server, here's what to do:
In Firefox 4 or 5, go to Tools > Sync setup. Say you want to create a new account. Say you want to use a custom server. That server is: https://weave.pirateship.org/

Fill in your username and password and you are golden. Don't worry, I will not be able to read your information.

Your rating: None Average: 4.8 (110 votes)

Updating diaspora

Updated: Mon, Jul 11, 2011 - 11:05pm
Type
Status
Concerning

Okay, I just updated my Diaspora server to the latest code. It's got some fancy updates like a "like" button.

I just wanted to make a quick note on updating it. This probably applies to any Rails app that uses Jammit.

After updating the code, make sure to delete the precompiled javascript and css files. Otherwise, you'll be running the new templates with the old css/js, and things will break.

The built assets live under public/assets. It's okay to delete them. Jammit will rebuild them from public/javascripts and public/stylesheets, based on the specification in config/assets.yml.

I should make sure that this instruction gets onto the Diaspora wiki.

Another quick note: I was finding that, in Firefox, the autoresize jquery plugin wasn't working correctly. It turns out that it was the fault of neither Firefox nor autoresize. It was the fault of the It's All Text firefox plugin I was running. I should make sure that the It's All Text people and the autoresize people are aware of this, but I'm not sure there's anything either of them can do.

Your rating: None Average: 3 (6 votes)

Wireless Mesh Networking

We've seen censorship of the internet in many countries lately. We're seeing poor regulation of the internet in the US. In order to fight this trend, we need to have the people own the network.

Enter Wireless Mesh Networking. People can buy low-cost wifi routers and keep them in their homes. Flash them with a special firmware based on OpenWRT and they can become a mesh network of their own. Communicate with friends and neighbors over the radio.

There have been several wireless mesh networking projects in many cities all around the world. I attended the Wireless Battle Mesh in Spain, so that I could figure out how to add Ann Arbor to that list.

Soon, there will be a real website about this. Until then, please read our mailing list.

Also, on the plane on the way back from the conference, I wrote up everything I knew into a handy guide.

Your rating: None Average: 3.5 (8 votes)

Vim modelines

Type
Status
Concerning

I edit a few different types of files with a few different indentation styles. Now, my favorite text editor is vim, and that supports using a config file to set your indentation style, but since I use a few of them, one config file won't work for me. Now, I'm sure you could also set a different style per filetype -- for example, python indents two spaces, C uses tabs displayed as four spaces. But that doesn't work, because different projects sometimes choose different styles, even though they use the same type of file.

Well, it turns out that you can store your preferred indentation setup (or any other vim commands) right there in the file you're working on. I've been aware of this for awhile, because I've seen the commands in files I've worked on. But I always forget the exact syntax of it. I usually end up looking for a file that has this, and then copying it out and tweaking it.

But now I know the magic word, so I could google it. These things are called "modelines". Here's a comprehensive howto on the vim wiki:
Modeline Magic

In short, here's how to do it (in a C file):

  1. // vim: ts=4:noet:sw=4

That's "tabstop = 4", "do not expand tabs (i.e. use tabs not spaces)", and "shift by four characters when using the >> command".

There. Now I know how to look this stuff up.

Your rating: None Average: 2 (5 votes)

Unattended DVD Ripping

Updated: Sun, Feb 14, 2010 - 2:42am
Type
Status
Concerning

I recently purchased a large number of movies from a very cool video store which was closing. It was a terrible disaster. Sure, I got a bunch of DVDs from it. But I did not gain access to any new movies -- I could have rented all these any time I wanted. Instead, the amazing collection in the possession of the video store is now split up in the hands of a bunch of individuals like me. It is not a collection anymore, and is far, far less useful to the community.

But this is a technical blog, not a library science blog. So where could I be headed with this?

Well, I don't want to sit around and import all these DVDs by hand. I want to set up a machine so that I can just drop a DVD in there and walk away, and have it rip the DVD for me. I got a headless computer and set it on my network. It's got a DVD drive, a large hard drive, and a samba server.

I set up ivman to respond to the dvd insertion event and to fire a script.

I edited /etc/ivman/IvmConfigActions.xml. Here is a stripped-down version of that file:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ivm:ActionsConfig version="0.2" xmlns:ivm="http://www.eikke.com/ivm">
  3.  
  4. <!-- This is what I added -->
  5. <ivm:Match name="hal.volume.disc.is_videodvd" value="true">
  6. <ivm:Option name="exec" value="/opt/ripdvd.sh '$hal.block.device$'" />
  7. </ivm:Match>
  8.  
  9. </ivm:ActionsConfig>

I based that entry off of a sample that was left in the comments of the Debian ivman package's version of IvmConfigActions.xml. The comment shows how to mount the dvd and then play it with mplayer.

My version of the file runs my ripdvd.sh script, which runs Handbrake to rip the dvd. I also decorate the script with some beeps to let me know that it's working, and eject the disc when done.

Note that, while Debian provides a Handbrake package, I installed it from source instead. In building it, it downloads the source code of all of its codec dependencies and builds them.

Here's the text of my ripdvd.sh script.

  1. #!/bin/sh
  2.  
  3. # Acknowledge that we are starting
  4. beep -f 1400 -l 250
  5.  
  6. DVD="$1"
  7. HANDBRAKE=/opt/handbrake/bin/HandBrakeCLI
  8. LOG="logger -p daemon.info -t ripdvd.sh "
  9. OUTDIR="/var/Data/Movies/Imports/"
  10.  
  11. function beep_success() {
  12. beep -f 750 -l 80 -r 5 -D 20
  13. }
  14.  
  15. function beep_failure() {
  16. beep -f 200 -l 900
  17. }
  18.  
  19. function dvdrip() {
  20. $LOG "Beginning rip of $DVD"
  21. local LABEL=`file -s "$DVD" | cut -f 2- -d "'" | cut -f 1 -d "'" | sed -e 's/ *$//'`
  22. $LOG "$DVD contains $LABEL"
  23. $LOG $HANDBRAKE --verbose 9 --input "$DVD" --longest --output "$OUTDIR/$LABEL.avi" --size 600 --native-language eng --aencoder lame
  24. $HANDBRAKE --verbose 9 --input "$DVD" --longest --output "$OUTDIR/$LABEL.avi" --size 600 --native-language eng --aencoder lame 2>&1 > $OUTDIR/HandBrake.log
  25. local STATUS="$?"
  26. $LOG "Finished importing $LABEL from $DVD (status = $STATUS)"
  27. return $STATUS
  28. }
  29.  
  30. if dvdrip; then
  31. beep_success
  32. else
  33. beep_failure
  34. fi
  35.  
  36. eject

I have also attached the ripdvd.sh script for download.

Your rating: None Average: 2.6 (5 votes)
AttachmentSize
ripdvd.sh.txt919 bytes

It's All Text!

Type
Status
Concerning

One of my favorite Firefox extensions these days is It's All Text!. This extension lets you use alternative text editors to edit the text in text boxes.

Now, big wimps like to edit their text in WYSIWYG editors. Not me. I like to use vim.

Okay, so "It's All Text!" allows us to give a command line to start a text editor. What command do we give to start vim?

Well, we could use gvim. But I vastly prefer to run vim in my normal gnome terminal.

Therefore, I created a script, /opt/termvim/termvim, which reads:

  1. #!/bin/sh
  2.  
  3. gnome-terminal --command="vim $@"

After making it executable, I can then set my editor in firefox by going to Tools > It's All Text! > Preferences. For "Editor", I set /opt/termvim/termvim.

Boom. I can use vim on the web.

Your rating: None Average: 3.1 (7 votes)

Fun with acpid

Updated: Fri, Feb 12, 2010 - 2:29am

It doesn't seem glamorous, but it is indeed possible to have fun with acpid. acpid is a daemon that sits and listens for acpi events from the kernel. When it gets an event, it fires the rules you have configured.

Okay, so what's the use?

Let's start with a simple one. On my laptop, when it's unplugged and I shut the lid, I want the laptop to suspend. Now, these days, I'm using Ubuntu on my laptop. Ubuntu is configured with this behavior automatically. But I've found that it doesn't always work. Also, even when it works, it can be slow. The sleep-when-lid-is-shut behavior is controlled by gnome-power-manager. It seems strange to me that we would have this behavior be controlled by something so far up the stack. X and gnome may have a lot on their minds. Also, I may not even be running X.

Fortunately, acpid is much lower in the stack. It'll be running, X or no.

So we can program the sleep behavior into the acpid config files.

In Ubuntu, we are interested in the directory /etc/acpi. There are a bunch of scripts there that are fired in response to acpi events. The ones we are interested in are /etc/acpi/lid.sh and /etc/acpi/power.sh. They respond to the lid and the AC adaptor, respectively. (In Arch Linux, there is only one script, /etc/acpi/handler.sh. Instead of editing files, you'll be editing different parts of that file).

You'll notice that /etc/acpi/lid.sh calls /etc/acpi/local/lid.sh.pre before it runs, and /etc/acpi/local/lid.sh.post when it's done. We will edit the "pre" file, so that we don't get in the way of new versions of acpid from the distro. Note that these "pre" and "post" scripts must be marked as executable, or the main scripts will ignore them and not run them.

This is what I put in the lid.sh.pre script:

  1. #!/bin/sh
  2.  
  3. grep -q closed /proc/acpi/button/lid/*/state
  4. if [ $? = 0 ] ; then
  5. grep -q off-line /proc/acpi/ac_adapter/*/state
  6. if [ $? = 0 ] ; then
  7. pm-suspend
  8. fi
  9. fi

With this script, when the lid is shut, it will check if the power is plugged in. If not, it'll put the computer to sleep. We also need to do it the other way: When the power plug is removed, we need to check if the lid is shut.

For this, we look at the power.sh script, which fires when the power plug is removed or put in.

Unfortunately, ubuntu does not provide a power.sh.pre and power.sh.post. That means that when we make changes to this file, we may have to be a little careful when we update acpid.

The first thing I did was to add lines to call power.sh.pre and power.sh.post to the power.sh script.

My power.sh script now reads:

  1. #!/bin/sh
  2.  
  3. test -f /usr/share/acpi-support/key-constants || exit 0
  4.  
  5. . /usr/share/acpi-support/policy-funcs
  6.  
  7. [ -x /etc/acpi/local/power.sh.pre ] && /etc/acpi/local/power.sh.pre
  8. if [ -z "$*" ] && [ `CheckPolicy` = 0 ]; then
  9. exit;
  10. fi
  11.  
  12. pm-powersave $*
  13. [ -x /etc/acpi/local/power.sh.post ] && /etc/acpi/local/power.sh.post

Putting these lines in here allows me to put the main part of the code in /etc/acpi/local/power.sh.pre. The text of this is exactly the same as the text of lid.sh.pre.

So that was pretty fun, right? I've got a couple more things here.

I had a pretty old computer that I was working on once. I often found that Firefox would run away with the processor and cause such high load that it would take a minute or more just to get access to a console to kill firefox.

So I used acpid to hook up the power button to automatically kill firefox. When firefox runs away with the cpu, I hit the power button. That runs the /etc/acpid/powerbtn.sh script.

Just like with power.sh, this does not have the "pre" and "post" hooks. Therefore, I add those hooks. Here is my powerbtn.sh script:

  1. #!/bin/sh
  2. # /etc/acpi/powerbtn.sh
  3. # Initiates a shutdown when the power putton has been
  4. # pressed.
  5.  
  6. # Skip if we just in the middle of resuming.
  7. test -f /var/lock/acpisleep && exit 0
  8.  
  9. [ -x /etc/acpi/local/powerbtn.sh.pre ] && /etc/acpi/local/powerbtn.sh.pre
  10. # If gnome-power-manager, kded4, dalston-power-applet or xfce4-power-manager
  11. # are running, let them handle policy This is effectively the same as
  12. # 'acpi-support's '/usr/share/acpi-support/policy-funcs' file.
  13.  
  14. if pidof gnome-power-manager kded4 dalston-power-applet xfce4-power-manager > /dev/null; then
  15. exit
  16. fi
  17. [ -x /etc/acpi/local/powerbtn.sh.post ] && /etc/acpi/local/powerbtn.sh.post
  18.  
  19. # If all else failed, just initiate a plain shutdown.
  20. /sbin/shutdown -h now "Power button pressed"

Then, in /etc/acpi/local/powerbtn.sh.pre, I add, simply,

  1. #!/bin/sh
  2. pkill firefox

So there you have it. Fun with acpid. That was fun, right?

Your rating: None Average: 3 (3 votes)

Drupal intra-linking problems solved

Updated: Thu, Feb 11, 2010 - 3:37pm
Type
Status
Concerning

Sometimes, when using Drupal, I find that I want to link to some of my older posts. However, I often have a couple of development checkouts of my site. Consider the case where I have the live site, which can be reached at http://iheartryan.com/, and I have a development checkout, http://localhost/iheartryan/. Now, say I'm trying to link to my Rhythmbox Playlist Folders post. How should I make this link? I have a couple choices:

  1. http://iheartryan.com/post/rhythmbox-playlist-folders
  2. /post/rhythmbox-playlist-folders

Neither of these will totally work for my purposes. For example, let's say I choose to make a full, absolute path, like #1 above, then when I look at this page in my development checkout, the link will point to the post on the live server. I can't navigate around internally on my dev checkout.

Choice #2 might help with that problem. It is a link to the post on whatever server I happen to be looking at. The problem is that my dev checkout has a different path. So choice #2 will give us a link to: http://localhost/post/rhythmbox-playlist-folders. What we need is a link to http://localhost/iheartryan/post/rhythmbox-playlist-folders.

I have a solution that will cover most of the cases. If you implement this solution on your site, then you can just make a link to /post/rhythmbox-playlist-folders, and it will be translated to /post/rhythmbox-playlist-folders, so it will work on any checkout.

To implement this solution, we will make some changes in a module and some changes in your site's theme.

First, the module. We need to have a module that implements hook_nodeapi. For all the sites I do, I always end up with some custom modifications, so let's say we have a module called "custom". (Technically, I could take the changes I'm about to present and release it as a module, but it is incomplete without the theme changes, and it doesn't seem like it should be a whole module release). In our custom module, I will implement hook_nodeapi:

  1. function custom_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  2. switch($op) {
  3. case 'view':
  4. $node->content['body']['#value'] = preg_replace_callback(
  5. '/<([^>]*)(src|href)="([^"]*)"/i',
  6. "_custom_twiddle_links",
  7. $node->content['body']['#value']);
  8.  
  9. break;
  10. }
  11. }
  12.  
  13. function _custom_twiddle_links($matches) {
  14. $url = $matches[3];
  15. if (preg_match('|^[a-z][^:]*://|', $url)) {
  16. // It's an absolute url. Return the whole thing as given.
  17. $retval = $matches[0];
  18. } elseif (strpos($url, "/") === 0) {
  19. // It's an absolute url on this host. Fix it with the right base path.
  20. $retval = "<{$matches[1]}{$matches[2]}=\"".base_path().ltrim($matches[3],"/")."\"";
  21. } else {
  22. // It's a relative url. Return the whole thing as given.
  23. $retval = $matches[0];
  24. }
  25.  
  26. return $retval;
  27. }

This will replace the links when displaying nodes.

There's still the problem of blocks. That's why we edit the site's theme.

We edit blocks.tpl.php to start with this:

  1. <?php
  2. /**
  3.  * If a link starts with /, it should be relative to the base_path of the site,
  4.  * not to the actual server root.
  5.  */
  6. if (!function_exists("_block_twiddle_links")) {
  7. function _block_twiddle_links($matches) {
  8. $url = $matches[3];
  9. if (preg_match('|^[a-z][^:]*://|', $url)) {
  10. // It's an absolute url. Return the whole thing as given.
  11. $retval = $matches[0];
  12. } elseif (strpos($url, "/") === 0) {
  13. // It's an absolute url on this host. Fix it with the right base path.
  14. $retval = "<{$matches[1]}{$matches[2]}=\"".base_path().ltrim($matches[3],"/")."\"";
  15. } else {
  16. // It's a relative url. Return the whole thing as given.
  17. $retval = $matches[0];
  18. }
  19.  
  20. return $retval;
  21. }
  22. }
  23.  
  24. if ($block->module == 'block') {
  25. $block->content = preg_replace_callback(
  26. '/<([^>]*)(src|href)="([^"]*)"/i',
  27. "_block_twiddle_links",
  28. $block->content);
  29. }
  30. ?>

This will fix the links for blocks.

That's the best I can do. This solution is not elegant. Perhaps there can be a a fix at the Drupal core level, so that we won't have to apply these dirty hacks. Perhaps this hack could be adjusted to have a token like [BASE_PATH], instead of working on all server-absolute links.

Perhaps there could even be a solution using the existing token module. Please tell me if you know of one.

Your rating: None Average: 3.5 (8 votes)