Apple

Compiling the IMAP extension for PHP 5.3.15 with OS X 10.8.3

I have been testing the Drupal support module locally which features the ability to create tickets from email messages to an IMAP inbox. It requires the imap_open() PHP function provided by the imap PHP extension, which unfortunately is not included in the OS X builds of PHP.

ivucica has published a wonderful script to his blog that compiles the IMAP extension without having to recompile PHP entirely, but unfortunately it was not working for me and nobody else seemed to have my problem either. Compiling the imap library and PCRE went very smoothly, but when it came time to build the PHP extension this error appeared during ./configure:

checking whether build with IMAP works... no
configure: error: build test failed. Please check the config.log for details.

Well, crap. I check config.log and determine it's a linking failure:

configure: program exited with status 1
configure: failed program was:
| /* confdefs.h */
| #define PACKAGE_NAME ""
| #define PACKAGE_TARNAME ""
| #define PACKAGE_VERSION ""
| #define PACKAGE_STRING ""
| #define PACKAGE_BUGREPORT ""
| #define PACKAGE_URL ""
| #define COMPILE_DL_IMAP 1
| #define HAVE_IMAP 1
| #define HAVE_IMAP2000 1
| #define HAVE_IMAP2004 1
| #define HAVE_NEW_MIME2TEXT 1
| #define HAVE_LIBPAM 1
| #define HAVE_IMAP_KRB 1
| #define HAVE_IMAP_SSL 1
| /* end confdefs.h.  */
|
|
| #if defined(__GNUC__) && __GNUC__ >= 4
| # define PHP_IMAP_EXPORT __attribute__ ((visibility("default")))
| #else
| # define PHP_IMAP_EXPORT
| #endif
|
|       PHP_IMAP_EXPORT void mm_log(void){}
|       PHP_IMAP_EXPORT void mm_dlog(void){}
|       PHP_IMAP_EXPORT void mm_flags(void){}
|       PHP_IMAP_EXPORT void mm_fatal(void){}
|       PHP_IMAP_EXPORT void mm_critical(void){}
|       PHP_IMAP_EXPORT void mm_nocritical(void){}
|       PHP_IMAP_EXPORT void mm_notify(void){}
|       PHP_IMAP_EXPORT void mm_login(void){}
|       PHP_IMAP_EXPORT void mm_diskerror(void){}
|       PHP_IMAP_EXPORT void mm_status(void){}
|       PHP_IMAP_EXPORT void mm_lsub(void){}
|       PHP_IMAP_EXPORT void mm_list(void){}
|       PHP_IMAP_EXPORT void mm_exists(void){}
|       PHP_IMAP_EXPORT void mm_searched(void){}
|       PHP_IMAP_EXPORT void mm_expunged(void){}
|       void rfc822_output_address_list(void);
|       void (*f)(void);
|       char foobar () {f = rfc822_output_address_list;}
|
|     char foobar();
|     int main() {
|       foobar();
|       return 0;
|     }
|
configure:6808: result: no
configure:6819: checking whether build with IMAP works
configure:6863: cc -o conftest -g -O2   conftest.c  -Wl,-rpath,/usr/local/imap-2007f/lib -L/usr/local/imap-2007f/lib -lc-client -lpam  -lkrb5  >&5
Undefined symbols for architecture x86_64:
  "_BIO_free", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_BIO_new_mem_buf", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_BIO_new_socket", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_ERR_error_string", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_genkey in libc-client.a(osdep.o)
  "_ERR_get_error", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_genkey in libc-client.a(osdep.o)
  "_ERR_load_crypto_strings", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_EVP_PKEY_free", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_PEM_read_bio_PrivateKey", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_PEM_read_bio_X509", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_RAND_seed", referenced from:
      _ssl_onceonlyinit in libc-client.a(osdep.o)
  "_RSA_generate_key", referenced from:
      _ssl_genkey in libc-client.a(osdep.o)
  "_SSL_CTX_ctrl", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_free", referenced from:
      _ssl_abort in libc-client.a(osdep.o)
  "_SSL_CTX_load_verify_locations", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_new", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_set_cipher_list", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_CTX_set_default_verify_paths", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_set_tmp_rsa_callback", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_CTX_set_verify", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_use_PrivateKey", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_use_RSAPrivateKey_file", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_CTX_use_certificate", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_use_certificate_chain_file", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_accept", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_ctrl", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_free", referenced from:
      _ssl_abort in libc-client.a(osdep.o)
  "_SSL_get_error", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_sout in libc-client.a(osdep.o)
  "_SSL_get_fd", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_server_input_wait in libc-client.a(osdep.o)
  "_SSL_get_peer_certificate", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_library_init", referenced from:
      _ssl_onceonlyinit in libc-client.a(osdep.o)
  "_SSL_load_error_strings", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_new", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_pending", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_server_input_wait in libc-client.a(osdep.o)
  "_SSL_read", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_server_input_wait in libc-client.a(osdep.o)
  "_SSL_set_bio", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_set_connect_state", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_set_fd", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_shutdown", referenced from:
      _ssl_abort in libc-client.a(osdep.o)
  "_SSL_state", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_write", referenced from:
      _ssl_start in libc-client.a(osdep.o)
      _ssl_sout in libc-client.a(osdep.o)
  "_SSLv23_client_method", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSLv23_server_method", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_TLSv1_client_method", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_TLSv1_server_method", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_X509_NAME_oneline", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_STORE_CTX_get_current_cert", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_STORE_CTX_get_error", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_free", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_X509_get_ext_d2i", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_X509_get_subject_name", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_verify_cert_error_string", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_sk_num", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_sk_value", referenced from:
      _ssl_start in libc-client.a(osdep.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I couldn't figure out why it wasn't picking up the symbols from libssl, even when manually trying to compile said file and adding a -lssl flag.

After an hour of struggling with it and my debugging efforts going nowhere, I try adding -lcrypto for the hell of it and it works!

tl;dr, if you get this error then simply replace the following line of the aformentioned script:

./configure --with-imap=/usr/local/imap-2007f --with-kerberos --with-imap-ssl

With the following line that adds the required linker flags:

LDFLAGS="-lssl -lcrypto" ./configure --with-imap=/usr/local/imap-2007f --with-kerberos --with-imap-ssl

That's it!

Syncing your iOS device (either iPod, iPhone or iPad) with multiple iTunes libraries

With Apple's changes surrounding iCloud, managing media on iOS devices has become much easier and it is no longer absolutely bound to a single computer. However, I have still found it tricky to sync a single iOS device with multiple computers (different iTunes libraries) without having to entirely wipe the device first.

I synchronize my iTunes library between two computers regularly, so both machines have up-to-date copy of all media files. Furthermore, I set them up with the same username (but different hostname), so the iTunes media file paths are identical between machines.

Searching Apple forums revealed this solution by turingtest2: apparently, each iTunes library is assigned a randomly-generated "library ID". This identifier is stored on the iOS device, so if it doens't match when connecting to a new computer, it will request to wipe the device before proceeding with the sync. Simply copying my entire ~/Music folder from one machine to the other was enough copy all the iTunes metadata (including the library ID) and my device now happily synchronizes on either machine. For those wanting to save time and not copy their entire ~/Music folder, see the forum post for the exact files you'll need to copy.

Reset sync history in OS X Lion without using iSync

In previous version of OS X, the iSync application could be used to reset sync history. This technique often resolved a bunch of miscellaneous bugs and sync-related problems. The iSync application has since been removed in OS X Lion and Apple's support documents aren't of much help. This one claims that resetting sync services can be performed from iSync in Leopard and from the Sync menu extra in later version of OS X. This document describes how to enable the menu extra in the MobileMe settings. Besides the fact that MobileMe is deprecate and no longer in use, the option described in that document to enable the Sync menu extra only appears after signing in to MobileMe... Which is deprecated.

Fortunately, there's a hidden folder you can open to enable any menu extra manually! Open Finder and select Go > Go to Folder (or press Command+Shift+G). In the prompt that appears, type: /System/Library/CoreServices/Menu Extras/. This should bring you to a folder with a bunch of menu extras that you can double-click to add to the menu bar. The one of interest in this case is Sync.menu, which should appear in your menu bar a second or two after opening it.

Once it's in your menu bar, hold Alt/Option and click on it to expose the advanced options and click "Reset Sync Services". After that's done, you can remove it from your menu bar if you'd like by holding Command and then clicking & dragging it out from the menu bar.

Disable Time Machine's local snapshots feature

Lion introduced a new Time Machine feature the OS would automatically create local snapshots of files so that even without a backup disk attached, you could still enter time machine and recover or undo changes to certain files. As your disk space becomes low, this space is automatically freed and the rate of snapshot creation is reduced.

This is great on desktops and I would assume the newer Macbook Pros that have better disks, but on my older MacBook Pro I've noticed that Safari has had trouble loading web pages and during this time, I hear the disk thrashing for about a minute or so and then all of a sudden the page loads. After a bit of researching and some help from the 'fs_usage' command, I discovered that a process called 'mtmd' was accessing the filesystem often and it turns out that mtmd is responsible for creating these local time machine snapshots. A quick Google revealed how to disable the local snapshot functionality:

sudo tmutil disablelocal

We'll see if that helps the disk thrashing! I did immediately recover 1GB of disk space though which is a good sign.

Installing the Darwin Calendar Server 2.4 on Fedora 13 or Fedora 14

As I mentioned in my last post, I've been playing with the Darwin Calendar Server (DCS) on Linux... Today I was able to re-test my setup notes to see if they worked properly, so below I've written a tutorial on how to get your own DCS server going on Fedora 13 or 14.

Installing Dependencies

Since we will be installing CalendarServer directly from the 2.4 branch subversion repository, the first thing to do is to install subversion and the dependencies for DCS:

su -
# Required to check out the source code from the repository
yum install subversion
# Dependencies
yum install patch memcached krb5-devel python-zope-interface PyXML pyOpenSSL python-kerberos
# Requirements for compiling xattr
yum install python-setuptools gcc gcc-c++ python-devel

Enable extended file attributes (xattrs)

DCS requires user extended file attributes so the user_xattr mount option must be enabled for the partition on which CalendarServer will be storing its documents and data (in this case, /srv). If you have not already enabled this option (it is disabled by default), edit /etc/fstab and add the user_xattr mount option after defaults, for example:

/dev/mapper/VolGroup-lv_root /                       ext4    defaults,user_xattr        1 1

Grab DCS from SVN and run auto-setup

Once these packages have been installed and extended file attributes have been enabled, we will begin setting up the CalendarServer as your regular, non-root user.

# Directory to hold CalendarServer checkout and its dependencies
mkdir CalendarServer
cd CalendarServer
# Checkout the code from the repo
svn checkout http://svn.calendarserver.org/repository/calendarserver/CalendarServer/tags/release/CalendarServer-2.4 CalendarServer-2.4
cd CalendarServer-2.4
# Start auto-setup
./run -s

Auto-setup will now attempt to grab any missing dependencies for CalendarServer an will unpack and patch them accordingly. You may find that the download for PyDirector stalls - if so, hit to abort setup and download it manually:

pushd ..
wget http://downloads.sourceforge.net/pythondirector/pydirector-1.0.0.tar.gz
tar xfz pydirector-1.0.0.tar.gz
popd
# Resume unpacking
./run -s

Prepare for installation

Since DCS bundles a modified version of Twisted as well as a few other projects (such as pydirector), we will now prepare an installation root folder to avoid conflicts with system libraries (i.e., Twisted if it has been installed from the Fedora repos). This code will be run as root.

su -
# setup data & document roots
mkdir -p /srv/CalendarServer/{Data,Documents}
chown -R daemon:daemon /srv/CalendarServer/
# setup installation root
mkdir -p /opt/CalendarServer/etc/caldavd
mkdir -p /opt/CalendarServer/var/run/caldavd
mkdir -p /opt/CalendarServer/var/log/caldavd

Install DCS and configure the server instance

The last step is to install DCS from the Subversion checkout we made earlier into the installation root. Replace /home/regularuser with the actual path to the home directory of your regular user.

# install DCS to installation root
cd /home/regularuser/CalendarServer/CalendarServer-2.4
./run -i /opt/CalendarServer
rm -rf /opt/CalendarServer/usr/caldavd/caldavd.plist
# copy sample configuration files
cp conf/servertoserver-test.xml /opt/CalendarServer/etc/caldavd/servertoserver.xml
cp conf/auth/accounts.xml /opt/CalendarServer/etc/caldavd/accounts.xml
cp conf/caldavd-test.plist /opt/CalendarServer/etc/caldavd/caldavd.plist
cp conf/sudoers.plist /opt/CalendarServer/etc/caldavd/sudoers.plist
# change permissions; passwords are stored plaintext!
chmod 600 /opt/CalendarServer/etc/caldavd/*

I have reported bugs #390 and #391 about problems with the setup script on 64-bit machines as well as a problem if a custom destination installation directory is used (which we did). This bit of code works around both of the bugs:

# 64-bit fix - see https://trac.calendarserver.org/ticket/391
sitelib="$(python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')"
sitearch="$(python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib(1))')"
if [ "$sitelib" != "$sitearch" ];then
  mv /opt/CalendarServer"${sitelib}"/twisted/plugins/caldav.py* /opt/CalendarServer"${sitearch}"/twisted/plugins
  # PYTHONPATH fix for 64-bit - see https://trac.calendarserver.org/ticket/390
  sed -i.orig 's|PYTHONPATH="'"${sitelib}"'|DESTDIR=/opt/CalendarServer\nPYTHONPATH="${DESTDIR}'"${sitelib}"':${DESTDIR}'"${sitearch}"':|' /opt/CalendarServer/usr/bin/caldavd
else
  # PYTHONPATH fix for 32-bit - see https://trac.calendarserver.org/ticket/390
  sed -i.orig 's|PYTHONPATH="'"${sitelib}"'|DESTDIR=/opt/CalendarServer\nPYTHONPATH="${DESTDIR}'"${sitelib}"':|' /opt/CalendarServer/usr/bin/caldavd
fi

If you would like your server to use SSL (highly recommended), you will need to generate a certificate. If you have a certificate and key ready to install, place it in /opt/CalendarServer/etc/tls. If not, you can easily generate a free self-signed one:

# Generate SSL keys
mkdir /opt/CalendarServer/etc/tls
openssl req -new -newkey rsa:1024 -days 365 -nodes -x509 -keyout www.example.com.key -out www.example.com.crt

Now, edit /opt/CalendarServer/etc/caldavd/caldavd.plist in your favorite editor and configure the server as follows:

    <!-- Network host name [empty = system host name] -->
    <key>ServerHostName</key>
    <string>example.com</string> <!-- The hostname clients use when connecting -->

# Data roots
    <!-- Data root -->
    <key>DataRoot</key>
    <string>/srv/CalendarServer/Data/</string>
        
    <!-- Document root -->
    <key>DocumentRoot</key>
    <string>/srv/CalendarServer/Documents/</string>

# Test accounts configuration
    <!-- XML File Directory Service -->
    <key>DirectoryService</key>
    <dict>
      <key>type</key>
      <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
         
      <key>params</key>
      <dict>
        <key>xmlFile</key>
        <string>/opt/CalendarServer/etc/caldavd/accounts.xml</string>
      </dict>
    </dict>

# Sudoers configuration
    <!-- Principals that can pose as other principals -->
    <key>SudoersFile</key>
    <string>/opt/CalendarServer/etc/caldavd/sudoers.plist</string>

# Delete this section
<!-- Wikiserver authentication (Mac OS X) -->
      <key>Wiki</key>
      <dict>
        <key>Enabled</key>
        <true/>
        <key>Cookie</key>
        <string>sessionID</string>
        <key>URL</key>
        <string>http://127.0.0.1/RPC2</string>
        <key>UserMethod</key>
        <string>userForSession</string>
        <key>WikiMethod</key>
        <string>accessLevelForUserWikiCalendar</string>
      </dict>

# logging
    <!--
        Logging
      -->

    <!-- Apache-style access log -->
    <key>AccessLogFile</key>
    <string>/opt/CalendarServer/var/log/caldavd/access.log</string>
    <key>RotateAccessLog</key>
    <false/>

    <!-- Server activity log -->
    <key>ErrorLogFile</key>
    <string>/opt/CalendarServer/var/log/caldavd/error.log</string>

    <!-- Log levels -->
    <key>DefaultLogLevel</key>
    <string>info</string> <!-- debug, info, warn, error -->
# a bit further down…
    <!-- Global server stats -->
    <key>GlobalStatsSocket</key>
    <string>/opt/CalendarServer/var/run/caldavd/caldavd-stats.sock</string>
# <snip>
    <!-- Server statistics file -->
    <key>ServerStatsFile</key>
    <string>/opt/CalendarServer/var/log/caldavd/stats.plist</string>
       
    <!-- Server process ID file -->
    <key>PIDFile</key>
    <string>/opt/CalendarServer/var/run/caldavd/caldavd.pid</string>

# SSL 
    <!-- Public key -->
    <key>SSLCertificate</key>
    <string>/opt/CalendarServer/etc/tls/www.example.com.crt</string>
           
    <!-- Private key -->
    <key>SSLPrivateKey</key> 
    <string>/opt/CalendarServer/etc/tls/www.example.com.key</string>

# Privilege drop
    <!--
        Process management
      -->
   
    <key>UserName</key>
    <string>daemon</string>
   
    <key>GroupName</key>
    <string>daemon</string>
       
    <key>ProcessType</key>
    <string>Combined</string>

# iSchedule server-to-server settings
      <!-- iSchedule protocol options -->
      <key>iSchedule</key>
      <dict>
        <key>Enabled</key>
        <false/>
        <key>AddressPatterns</key>
        <array>
        </array>
        <key>Servers</key>
        <string>/opt/CalendarServer/etc/caldavd/servertoserver.xml</string>
      </dict>

# Communication socket
    <!-- A unix socket used for communication between the child and master processes.
         An empty value tells the server to use a tcp socket instead. -->
    <key>ControlSocket</key>
    <string>/opt/CalendarServer/var/run/caldavd/caldavd.sock</string>

# Twisted
    <!--
        Twisted
      -->
        
    <key>Twisted</key>
    <dict>
      <key>twistd</key>
      <string>/opt/CalendarServer/usr/bin/twistd</string>
    </dict>

# Load balancer
    <!--
        Python Director
      -->

    <key>PythonDirector</key>
    <dict>
      <key>pydir</key>
      <string>/opt/CalendarServer/usr/bin/pydir.py</string>

      <key>ConfigFile</key>
      <string>/opt/CalendarServer/etc/pydir.xml</string>

      <key>ControlSocket</key>
      <string>/opt/CalendarServer/var/run/caldavd/caldavd-pydir.sock</string>
    </dict>

...Profit!

Try starting the server!

/opt/CalendarServer/usr/bin/caldavd -T /opt/CalendarServer/usr/bin/twistd -f /opt/CalendarServer/etc/caldavd/caldavd.plist -X

If all goes well, press to kill the process and then daemonize it:

/opt/CalendarServer/usr/bin/caldavd -T /opt/CalendarServer/usr/bin/twistd -f /opt/CalendarServer/etc/caldavd/caldavd.plist