diff -Nru clamz-0.3/clamz.1 clamz-0.4/clamz.1 --- clamz-0.3/clamz.1 2010-04-01 03:59:38.000000000 +0100 +++ clamz-0.4/clamz.1 2010-05-01 03:37:41.000000000 +0100 @@ -1,4 +1,4 @@ -.TH clamz 1 "March 2010" "Clamz 0.3" +.TH clamz 1 "April 2010" "Clamz 0.4" .SH NAME clamz \- download MP3 music files from Amazon.com .SH SYNOPSIS @@ -90,8 +90,14 @@ .SS FORMAT VARIABLES As part of a \fIname-format\fR or \fIdirectory-format\fR option, you -may include references to environment variables (e.g., \fB$HOME\fR) as -well as any of the following special variables: +may include references to environment variables (e.g., \fB$HOME\fR) or +to the `xdg-user-dirs' configuration variables (e.g., +\fB$XDG_MUSIC_DIR\fR or \fB$XDG_DESKTOP_DIR\fR.) + +In addition, the following special variables are defined for each +track, based on the information provided in the AMZ file, and subject +to the above configuration options (\fB--forbid-chars\fR, +\fB--forbid-uppercase\fR, etc.) .TP \fB${title}\fR, \fB${creator}\fR, \fB${tracknum}\fR, \fB${discnum}\fR, \fB${genre}\fR, \fB${asin}\fR Title, creator, track number, disc number, genre, and ASIN (Amazon @@ -109,6 +115,18 @@ These variables formerly contained metadata for the AMZ file as a whole; current AMZ files do not contain this information. Using these variables is not recommended. +.PP +Similar to shell variable expansion, you can also use the following +conditional expressions: +.TP +\fB${\fIvar\fB:-\fIstring\fB}\fR +Expands to the value of variable \fIvar\fR if it is defined and +non-empty; otherwise, expands to \fIstring\fR (which may itself +contain variable references.) +.TP +\fB${\fIvar\fB:+\fIstring\fB}\fR +Expands to \fIstring\fR if the variable \fIvar\fR is defined and +non-empty; otherwise, expands to an empty string. .SH FILES .TP @@ -119,6 +137,9 @@ .TP $HOME/.clamz/amzfiles/ Directory containing backup copies of AMZ files. +.TP +$HOME/.clamz/logs/ +Directory containing log files. .SH ENVIRONMENT .TP diff -Nru clamz-0.3/clamz.c clamz-0.4/clamz.c --- clamz-0.3/clamz.c 2010-04-01 01:42:54.000000000 +0100 +++ clamz-0.4/clamz.c 2010-05-01 03:15:01.000000000 +0100 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -96,16 +97,29 @@ } } +static const char *getbasename(const char *fname) +{ + const char *b; + + while ((b = strchr(fname, '/'))) + fname = b + 1; + + return fname; +} + static int run_amz_file(clamz_downloader *dl, const clamz_config *cfg, FILE *amzfile, const char *fname) { char *inbuf; + unsigned char *xml; size_t sz; clamz_playlist *pl; int i; int status, rv = 0; + char *logname; + FILE *logfile; sz = 0; inbuf = NULL; @@ -122,8 +136,7 @@ fclose(amzfile); if (cfg->printonly && cfg->printasxml) { - unsigned char *xml = decrypt_amz_file(inbuf, sz, fname); - + xml = decrypt_amz_file(inbuf, sz, fname); if (!xml) { free(inbuf); return 2; @@ -133,11 +146,10 @@ free(xml); free(inbuf); return 0; - - } else { - + } + else { if (!cfg->printonly) { - if (write_backup_file(inbuf, sz, fname)) { + if (write_backup_file(inbuf, sz, getbasename(fname))) { free(inbuf); return 3; } @@ -151,9 +163,30 @@ return 2; } + if (!cfg->printonly) { + logname = get_config_file_name("logs", getbasename(fname), ".log"); + if (!logname) { + free(inbuf); + return 1; + } + + logfile = fopen(logname, "w"); + if (!logfile) { + perror(logname); + free(logname); + free(inbuf); + return 3; + } + + free(logname); + set_download_log_file(dl, logfile); + } + else { + logfile = NULL; + } + if (cfg->printonly || cfg->verbose) print_pl_info(pl, fname); - for (i = 0; i < pl->num_tracks; i++) { if (cfg->printonly || cfg->verbose) @@ -165,12 +198,105 @@ fputc('\n', stderr); } + set_download_log_file(dl, NULL); + free(inbuf); free_playlist(pl); + if (logfile) + fclose(logfile); return rv; } } +/* Parse XDG user-dirs configuration file and set environment + variables (XDG_DESKTOP_DIR, XDG_MUSIC_DIR, etc.) */ +static void set_xdg_user_dirs() +{ + const char *p, *q, *home; + char *cfgfname; + FILE *cfgfile; + char buf[1024]; + char *envstr; + + home = getenv("HOME"); + if (!home) + return; + + if ((p = getenv("XDG_CONFIG_HOME"))) { + cfgfname = strdup(p); + if (!cfgfname) + return; + } + else { + cfgfname = strdup(home); + if (!cfgfname) + return; + if (concatenate(&cfgfname, "/.config", strlen("/.config"))) { + free(cfgfname); + return; + } + } + + if (concatenate(&cfgfname, "/user-dirs.dirs", strlen("/user-dirs.dirs"))) { + free(cfgfname); + return; + } + + cfgfile = fopen(cfgfname, "r"); + free(cfgfname); + if (!cfgfile) + return; + + while (fgets(buf, sizeof(buf), cfgfile)) { + p = buf; + while (*p == ' ' || *p == '\t') + p++; + if (strncmp(p, "XDG_", 4)) + continue; + + q = p; + while (isalnum((int) (unsigned char) *q) || *q == '_') + q++; + if (q == p || q[0] != '=' || q[1] != '"') + continue; + + q++; + envstr = NULL; + if (concatenate(&envstr, p, q - p)) { + free(envstr); + fclose(cfgfile); + return; + } + + q++; + if (!strncmp(q, "$HOME", 5)) { + if (concatenate(&envstr, home, strlen(home))) { + free(envstr); + fclose(cfgfile); + return; + } + q += 5; + } + + while (*q && *q != '"') { + if (q[0] == '\\' && q[1]) + q++; + if (concatenate(&envstr, q, 1)) { + free(envstr); + fclose(cfgfile); + return; + } + q++; + } + + putenv(envstr); + } + + fclose(cfgfile); +} + +static int utf8locale; + int main(int argc, char **argv) { clamz_config cfg; @@ -193,9 +319,11 @@ setlocale(LC_ALL, ""); str = nl_langinfo(CODESET); if (!strcasecmp(str, "UTF-8") || !strcasecmp(str, "UTF8")) - cfg.utf8locale = 1; + utf8locale = cfg.utf8locale = 1; else - cfg.utf8locale = 0; + utf8locale = cfg.utf8locale = 0; + + set_xdg_user_dirs(); if (parse_args(&argc, argv, &cfg)) { if (cfg.output_dir) free(cfg.output_dir); @@ -298,7 +426,7 @@ void print_progress(const clamz_track *tr, const char *filename, int progress) { - int i; + int i, j; const char *name; fputc('\r', stderr); @@ -308,8 +436,23 @@ else name = filename; - for (i = 0; i < 32 && name[i]; i++) - fputc(name[i], stderr); + for (i = j = 0; i < 32 && name[j]; i++) { + if ((unsigned char) name[j] & 0x80) { + if (!utf8locale) + fputc('?', stderr); + + do { + if (utf8locale) + fputc(name[j], stderr); + j++; + } while (((unsigned char) name[j] & 0xc0) == 0x80); + } + else { + fputc(name[j], stderr); + j++; + } + } + for (; i < 32; i++) fputc(' ', stderr); diff -Nru clamz-0.3/clamz.desktop clamz-0.4/clamz.desktop --- clamz-0.3/clamz.desktop 2010-04-01 04:18:04.000000000 +0100 +++ clamz-0.4/clamz.desktop 2010-04-30 03:52:29.000000000 +0100 @@ -2,7 +2,7 @@ Encoding=UTF-8 Name=Clamz MP3 Downloader (command-line) Comment=Download MP3 files from AmazonMP3.com -Exec=clamz "--default-output-dir=\\$HOME/Music/\\${album_artist}/\\${album}" +Exec=clamz "--default-output-dir=\\${XDG_MUSIC_DIR:-\\$HOME/Music}/\\${album_artist}/\\${album}" Terminal=true Type=Application Categories=Network; diff -Nru clamz-0.3/clamz.h clamz-0.4/clamz.h --- clamz-0.3/clamz.h 2010-03-31 10:48:44.000000000 +0100 +++ clamz-0.4/clamz.h 2010-04-22 18:14:37.000000000 +0100 @@ -100,12 +100,18 @@ const char *fname); /* options.c */ -char *get_config_file_name(const char *name); +char *get_config_file_name(const char *subdir, const char *name, + const char *suffix); int parse_args(int *argc, char **argv, clamz_config *cfg); +/* vars.c */ +int expand_file_name(const clamz_config *cfg, const clamz_track *tr, + char **filename, const char *format); + /* download.c */ clamz_downloader *new_downloader(const clamz_config *cfg); void free_downloader(clamz_downloader *dl); +void set_download_log_file(clamz_downloader *dl, FILE *log); int download_track(clamz_downloader *dl, clamz_track *tr); /* clamz.c */ diff -Nru clamz-0.3/configure clamz-0.4/configure --- clamz-0.3/configure 2010-04-01 02:17:45.000000000 +0100 +++ clamz-0.4/configure 2010-04-29 04:57:49.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.65 for Clamz 0.3. +# Generated by GNU Autoconf 2.65 for Clamz 0.4. # # Report bugs to . # @@ -552,8 +552,8 @@ # Identity of this package. PACKAGE_NAME='Clamz' PACKAGE_TARNAME='clamz' -PACKAGE_VERSION='0.3' -PACKAGE_STRING='Clamz 0.3' +PACKAGE_VERSION='0.4' +PACKAGE_STRING='Clamz 0.4' PACKAGE_BUGREPORT='floppusmaximus@users.sf.net' PACKAGE_URL='' @@ -1218,7 +1218,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Clamz 0.3 to adapt to many kinds of systems. +\`configure' configures Clamz 0.4 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1279,7 +1279,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Clamz 0.3:";; + short | recursive ) echo "Configuration of Clamz 0.4:";; esac cat <<\_ACEOF @@ -1384,7 +1384,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Clamz configure 0.3 +Clamz configure 0.4 generated by GNU Autoconf 2.65 Copyright (C) 2009 Free Software Foundation, Inc. @@ -1649,7 +1649,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Clamz $as_me 0.3, which was +It was created by Clamz $as_me 0.4, which was generated by GNU Autoconf 2.65. Invocation command line was $ $0 $@ @@ -4515,7 +4515,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Clamz $as_me 0.3, which was +This file was extended by Clamz $as_me 0.4, which was generated by GNU Autoconf 2.65. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4577,7 +4577,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Clamz config.status 0.3 +Clamz config.status 0.4 configured by $0, generated by GNU Autoconf 2.65, with options \\"\$ac_cs_config\\" diff -Nru clamz-0.3/configure.ac clamz-0.4/configure.ac --- clamz-0.3/configure.ac 2009-07-18 02:58:59.000000000 +0100 +++ clamz-0.4/configure.ac 2010-04-29 04:57:42.000000000 +0100 @@ -1,5 +1,5 @@ AC_PREREQ(2.61) -AC_INIT([Clamz], [0.3], [floppusmaximus@users.sf.net]) +AC_INIT([Clamz], [0.4], [floppusmaximus@users.sf.net]) AC_CONFIG_SRCDIR([clamz.h]) AC_CONFIG_HEADER([config.h]) diff -Nru clamz-0.3/debian/changelog clamz-0.4/debian/changelog --- clamz-0.3/debian/changelog 2010-04-11 09:49:22.000000000 +0100 +++ clamz-0.4/debian/changelog 2010-05-23 18:44:12.000000000 +0100 @@ -1,3 +1,10 @@ +clamz (0.4-1) lucid; urgency=low + + * New upstream release + * added libgcrypt11, libcurl3, libexpat1 as dependencies + + -- Dabang Sat, 23 May 2010 19:32:00 +0200 + clamz (0.3-2) lucid; urgency=low * Added patch to remove update-mime-database and update-desktop- diff -Nru clamz-0.3/debian/control clamz-0.4/debian/control --- clamz-0.3/debian/control 2010-04-11 09:25:30.000000000 +0100 +++ clamz-0.4/debian/control 2010-05-23 18:44:12.000000000 +0100 @@ -8,6 +8,6 @@ Package: clamz Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, shared-mime-info, desktop-file-utils +Depends: ${shlibs:Depends}, ${misc:Depends}, shared-mime-info, desktop-file-utils, libgcrypt11, libcurl3, libexpat1 Description: Clamz is a little command-line program to download MP3 files from Amazon.com's music store Clamz is a little command-line program to download MP3 files from Amazon.com's music store. It is intended to serve as a substitute for Amazon's official MP3 Downloader, which is not free software (and therefore is only available in binary form for a limited set of platforms.) Clamz can be used to download either individual songs or complete albums that you have purchased from Amazon. diff -Nru clamz-0.3/debian/patches/01_Makefile.dpatch clamz-0.4/debian/patches/01_Makefile.dpatch --- clamz-0.3/debian/patches/01_Makefile.dpatch 2010-04-10 19:04:20.000000000 +0100 +++ clamz-0.4/debian/patches/01_Makefile.dpatch 2010-05-23 18:52:49.000000000 +0100 @@ -1,14 +1,14 @@ #! /bin/sh /usr/share/dpatch/dpatch-run -## 01_Makefile.dpatch by Dabang +## 01_Makefile.dpatch by ## ## All lines beginning with `## DP:' are a description of the patch. -## DP: removing 'update-mime database' from makefile +## DP: removing update-mime-database and update-desktop-database from makefile. @DPATCH@ -diff -urNad clamz-0.3~/Makefile.in clamz-0.3/Makefile.in ---- clamz-0.3~/Makefile.in 2010-04-01 03:30:17.000000000 +0200 -+++ clamz-0.3/Makefile.in 2010-04-10 20:01:56.067907980 +0200 -@@ -67,12 +67,12 @@ +diff -urNad clamz-0.4~/Makefile.in clamz-0.4/Makefile.in +--- clamz-0.4~/Makefile.in 2010-04-29 05:49:49.000000000 +0200 ++++ clamz-0.4/Makefile.in 2010-05-23 19:49:59.459750780 +0200 +@@ -70,12 +70,12 @@ install-desktop: $(INSTALL) -d -m 755 $(DESTDIR)$(applications_dir) $(INSTALL) -m 644 $(srcdir)/clamz.desktop $(DESTDIR)$(applications_dir) @@ -23,7 +23,7 @@ ## Uninstallation ## -@@ -84,11 +84,11 @@ +@@ -87,11 +87,11 @@ uninstall-desktop: rm -f $(DESTDIR)$(applications_dir)/clamz.desktop diff -Nru clamz-0.3/debian/tmp/DEBIAN/control clamz-0.4/debian/tmp/DEBIAN/control --- clamz-0.3/debian/tmp/DEBIAN/control 2010-04-11 09:51:32.000000000 +0100 +++ clamz-0.4/debian/tmp/DEBIAN/control 2010-05-23 18:53:43.000000000 +0100 @@ -1,8 +1,8 @@ Package: clamz -Version: 0.3-2 -Architecture: i386 +Version: 0.4-1 +Architecture: amd64 Maintainer: Dabang -Installed-Size: 116 +Installed-Size: 120 Depends: libc6 (>= 2.4), libcurl3 (>= 7.16.2-1), libexpat1 (>= 1.95.8), libgcrypt11 (>= 1.4.2), shared-mime-info, desktop-file-utils Section: misc Priority: extra diff -Nru clamz-0.3/debian/tmp/DEBIAN/md5sums clamz-0.4/debian/tmp/DEBIAN/md5sums --- clamz-0.3/debian/tmp/DEBIAN/md5sums 2010-04-11 09:51:32.000000000 +0100 +++ clamz-0.4/debian/tmp/DEBIAN/md5sums 2010-05-23 18:53:43.000000000 +0100 @@ -1,8 +1,8 @@ -9602a26c2524016e375556bd3bc246fa usr/share/doc/clamz/changelog.Debian.gz +84e367a34879f441b4847b6259fd3eea usr/share/man/man1/clamz.1.gz +746c37fcf17db3cf16686655f88bd1c5 usr/share/mime/packages/clamz.xml +17a83548e3c79234e91b4a85a55b903a usr/share/applications/clamz.desktop 8d2de7badae35d3c7140c82fa5d25649 usr/share/doc/clamz/COPYING.gz -6bd13e7e01f7ec54b449b972133ddddb usr/share/doc/clamz/README.gz +4c2d1507b4cbbb3aa8c2ad4a64377c8e usr/share/doc/clamz/README.gz 5ebb5e6ed7d1f1eeed39875a50479925 usr/share/doc/clamz/copyright -746c37fcf17db3cf16686655f88bd1c5 usr/share/mime/packages/clamz.xml -62393c284b30d2bbecd23a606baf5eae usr/share/man/man1/clamz.1.gz -309b539a5df27c985c9b28d952f1202b usr/share/applications/clamz.desktop -e0e22ccd20f3d78ad2d790d841ff8473 usr/bin/clamz +467e74ed6fac873ea5362ba680eeb22d usr/share/doc/clamz/changelog.Debian.gz +443a5f6281dc0836dba58ede377a481b usr/bin/clamz Binary files /tmp/P7EYdUAptB/clamz-0.3/debian/tmp/usr/bin/clamz and /tmp/12YaUe11qM/clamz-0.4/debian/tmp/usr/bin/clamz differ diff -Nru clamz-0.3/debian/tmp/usr/share/applications/clamz.desktop clamz-0.4/debian/tmp/usr/share/applications/clamz.desktop --- clamz-0.3/debian/tmp/usr/share/applications/clamz.desktop 2010-04-11 09:51:22.000000000 +0100 +++ clamz-0.4/debian/tmp/usr/share/applications/clamz.desktop 2010-05-23 18:53:39.000000000 +0100 @@ -2,7 +2,7 @@ Encoding=UTF-8 Name=Clamz MP3 Downloader (command-line) Comment=Download MP3 files from AmazonMP3.com -Exec=clamz "--default-output-dir=\\$HOME/Music/\\${album_artist}/\\${album}" +Exec=clamz "--default-output-dir=\\${XDG_MUSIC_DIR:-\\$HOME/Music}/\\${album_artist}/\\${album}" Terminal=true Type=Application Categories=Network; Binary files /tmp/P7EYdUAptB/clamz-0.3/debian/tmp/usr/share/doc/clamz/changelog.Debian.gz and /tmp/12YaUe11qM/clamz-0.4/debian/tmp/usr/share/doc/clamz/changelog.Debian.gz differ Binary files /tmp/P7EYdUAptB/clamz-0.3/debian/tmp/usr/share/doc/clamz/README.gz and /tmp/12YaUe11qM/clamz-0.4/debian/tmp/usr/share/doc/clamz/README.gz differ Binary files /tmp/P7EYdUAptB/clamz-0.3/debian/tmp/usr/share/man/man1/clamz.1.gz and /tmp/12YaUe11qM/clamz-0.4/debian/tmp/usr/share/man/man1/clamz.1.gz differ diff -Nru clamz-0.3/download.c clamz-0.4/download.c --- clamz-0.3/download.c 2010-04-01 04:40:37.000000000 +0100 +++ clamz-0.4/download.c 2010-04-22 18:20:40.000000000 +0100 @@ -27,7 +27,6 @@ #include #include #include -#include #include @@ -40,6 +39,9 @@ int outfd; clamz_track *track; int last_progress; + curl_off_t startpos; + char error_buf[CURL_ERROR_SIZE]; + FILE *log_file; }; /* Initialize downloader state */ @@ -47,6 +49,7 @@ { clamz_downloader *dl = malloc(sizeof(clamz_downloader)); char *cookiejar; + char useragent[100]; if (!dl) { print_error("Out of memory"); @@ -62,13 +65,16 @@ return NULL; } + curl_easy_setopt(dl->curl, CURLOPT_ERRORBUFFER, dl->error_buf); curl_easy_setopt(dl->curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(dl->curl, CURLOPT_FAILONERROR, 1L); curl_easy_setopt(dl->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(dl->curl, CURLOPT_COOKIEFILE, ""); - curl_easy_setopt(dl->curl, CURLOPT_USERAGENT, PACKAGE_STRING); - cookiejar = get_config_file_name("cookies"); + sprintf(useragent, "Amazon MP3 Downloader (%s)", PACKAGE_STRING); + curl_easy_setopt(dl->curl, CURLOPT_USERAGENT, useragent); + + cookiejar = get_config_file_name(NULL, "cookies", NULL); if (cookiejar) { curl_easy_setopt(dl->curl, CURLOPT_COOKIEJAR, cookiejar); free(cookiejar); @@ -96,176 +102,59 @@ free(dl); } -/* Convert a string according to user's preferences */ -static char *convert_string(const clamz_config *cfg, const char *s) +/* Callback for debug info logging */ +static int write_debug_info(CURL *curl UNUSED, curl_infotype type, char *text, + size_t length, void *data) { - const unsigned char *us = (const unsigned char*) s; - char *converted; - int len; - int i, j; + clamz_downloader *dl = data; - len = strlen(s) + 1; - converted = malloc(len * sizeof(char)); + if (dl->log_file) { + switch (type) { + case CURLINFO_TEXT: + fprintf(dl->log_file, "* "); + fwrite(text, 1, length, dl->log_file); + fflush(dl->log_file); + break; - if (!converted) { - print_error("Out of memory"); - return NULL; - } + case CURLINFO_HEADER_IN: + fprintf(dl->log_file, "< "); + fwrite(text, 1, length, dl->log_file); + fflush(dl->log_file); + break; - i = j = 0; - while (s[i]) { - if (j >= len - 1) { - len *= 2; - converted = realloc(converted, len * sizeof(char)); - - if (!converted) { - print_error("Out of memory"); - return NULL; - } - } + case CURLINFO_HEADER_OUT: + fprintf(dl->log_file, "> "); + fwrite(text, 1, length, dl->log_file); + fflush(dl->log_file); + break; - if (us[i] & 0x80) { - if (cfg->allowutf8) { - converted[j++] = s[i++]; - } - else { - converted[j++] = '_'; - do - i++; - while (((unsigned char) s[i] & 0xc0) == 0x80); - } - } - else if (s[i] == '/' || iscntrl(us[i])) { - converted[j++] = '_'; - i++; - } - else if (isupper(us[i]) && !cfg->allowupper) { - converted[j++] = tolower(s[i++]); - } - else if (cfg->forbid_chars && strchr(cfg->forbid_chars, s[i])) { - converted[j++] = '_'; - i++; - } - else { - converted[j++] = s[i++]; + default: + /* ignore other types of log message */ + break; } } - converted[j] = 0; - - return converted; + return 0; } -static int append_subst_str(char **filename, const char *format, - clamz_downloader *dl) +/* Write curl log to given file */ +void set_download_log_file(clamz_downloader *dl, FILE *log) { - const char *p, *q; - const char *f = format; - const char *value; - char *var, *converted; - int subst_raw; - - while (*f) { - if ((p = strchr(f, '$'))) { - if (p != f - && concatenate(filename, f, p - f)) - return 1; - - p++; - if (*p == '{') { - p++; - q = strchr(p, '}'); - if (!q) { - print_error("Invalid variable reference in '%s'", format); - return 1; - } - f = q + 1; - } - else { - q = p; - while (isalnum((int) (unsigned char) *q) || *q == '_') - q++; - f = q; - } + dl->log_file = log; - subst_raw = 0; - - if (p == q) - value = "$"; - else { - var = NULL; - if (concatenate(&var, p, q - p)) - return 1; - - if (!strcasecmp(var, "title")) - value = dl->track->title; - else if (!strcasecmp(var, "creator")) - value = dl->track->creator; - else if (!strcasecmp(var, "album")) - value = dl->track->album; - else if (!strcasecmp(var, "tracknum")) { - value = dl->track->trackNum; - if (value[0] && !value[1]) { - if (concatenate(filename, "0", 1)) - return 1; - } - } - else if (!strcasecmp(var, "album_artist")) - value = find_meta(dl->track->meta, TMETA_ALBUM_ARTIST); - else if (!strcasecmp(var, "genre")) - value = find_meta(dl->track->meta, TMETA_GENRE); - else if (!strcasecmp(var, "discnum")) - value = find_meta(dl->track->meta, TMETA_DISC_NUM); - else if (!strcasecmp(var, "suffix")) { - value = find_meta(dl->track->meta, TMETA_TRACK_TYPE); - if (!value) - value = "mp3"; - } - else if (!strcasecmp(var, "asin")) - value = find_meta(dl->track->meta, TMETA_ASIN); - else if (!strcasecmp(var, "album_asin")) - value = find_meta(dl->track->meta, TMETA_ALBUM_ASIN); - else if (!strcasecmp(var, "amz_title")) - value = dl->track->playlist->title; - else if (!strcasecmp(var, "amz_creator")) - value = dl->track->playlist->creator; - else if (!strcasecmp(var, "amz_asin")) - value = find_meta(dl->track->playlist->meta, PMETA_ASIN); - else if (!strcasecmp(var, "amz_genre")) - value = find_meta(dl->track->playlist->meta, PMETA_GENRE); - else { - value = getenv(var); - subst_raw = 1; - } - - free(var); - } - - if (value) { - if (subst_raw) { - if (concatenate(filename, value, strlen(value))) - return 1; - } - else { - converted = convert_string(dl->cfg, value); - if (!converted) - return 1; - if (concatenate(filename, converted, strlen(converted))) { - free(converted); - return 1; - } - free(converted); - } - } + if (dl->curl) { + if (log) { + curl_easy_setopt(dl->curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(dl->curl, CURLOPT_DEBUGFUNCTION, write_debug_info); + curl_easy_setopt(dl->curl, CURLOPT_DEBUGDATA, dl); } else { - return concatenate(filename, f, strlen(f)); + curl_easy_setopt(dl->curl, CURLOPT_VERBOSE, 0); } } - - return 0; } +/* Create parent directories if they do not already exist */ static int create_parents(char *filename) { char *p; @@ -289,7 +178,7 @@ return 0; } - +/* Callback for writing downloaded data to the output file */ static size_t write_output(void *ptr, size_t size, size_t n, void *data) { clamz_downloader *dl = data; @@ -304,7 +193,7 @@ return r; } - +/* Callback for displaying progress of transfer */ static int show_progress(void *data, double dltotal, double dlnow, double ultotal UNUSED, double ulnow UNUSED) { @@ -314,10 +203,14 @@ if (dl->cfg->quiet) return 0; - if (dltotal > 0) + if (dltotal > 0) { + dlnow += dl->startpos; + dltotal += dl->startpos; progress = (int) (100 * dlnow / dltotal); - else + } + else { progress = -1; + } if (progress != dl->last_progress) { dl->last_progress = progress; @@ -332,7 +225,6 @@ int i; char *s; CURLcode err; - off_t startpos; if (!tr->location) { print_error("No URL provided for this track"); @@ -348,17 +240,18 @@ /* ignore output_dir if name_format is an absolute path */ if (dl->cfg->output_dir && (!dl->cfg->name_format || dl->cfg->name_format[0] != '/')) { - if (append_subst_str(&dl->filename, dl->cfg->output_dir, dl)) + if (expand_file_name(dl->cfg, tr, &dl->filename, dl->cfg->output_dir)) return 1; - if (append_subst_str(&dl->filename, "/", dl)) + if (expand_file_name(dl->cfg, tr, &dl->filename, "/")) return 1; } if (dl->cfg->name_format) { - if (append_subst_str(&dl->filename, dl->cfg->name_format, dl)) + if (expand_file_name(dl->cfg, tr, &dl->filename, dl->cfg->name_format)) return 1; } - else { + + if (!dl->filename || !dl->filename[0]) { print_error("No output filename specified"); return 1; } @@ -428,9 +321,8 @@ curl_easy_setopt(dl->curl, CURLOPT_URL, tr->location); - startpos = lseek(dl->outfd, (off_t) 0, SEEK_END); - curl_easy_setopt(dl->curl, CURLOPT_RESUME_FROM_LARGE, - (curl_off_t) startpos); + dl->startpos = lseek(dl->outfd, (off_t) 0, SEEK_END); + curl_easy_setopt(dl->curl, CURLOPT_RESUME_FROM_LARGE, dl->startpos); err = curl_easy_perform(dl->curl); @@ -439,7 +331,7 @@ break; } - if (startpos != 0 && err == CURLE_HTTP_RANGE_ERROR) { + if (dl->startpos != 0 && err == CURLE_HTTP_RANGE_ERROR) { /* assume that this means we've already downloaded the whole thing... I guess */ print_progress(tr, dl->filename, 100); @@ -447,7 +339,7 @@ break; } - print_error("Error downloading file: %s", curl_easy_strerror(err)); + print_error("Error downloading file: %s", dl->error_buf); if (i < dl->cfg->maxattempts) sleep(2); diff -Nru clamz-0.3/Makefile.in clamz-0.4/Makefile.in --- clamz-0.3/Makefile.in 2010-04-11 10:02:54.000000000 +0100 +++ clamz-0.4/Makefile.in 2010-05-23 19:04:16.000000000 +0100 @@ -30,7 +30,7 @@ VPATH = @srcdir@ distname = @PACKAGE_TARNAME@-@PACKAGE_VERSION@ -distfiles = clamz.c playlist.c options.c download.c clamz.h \ +distfiles = clamz.c playlist.c options.c download.c vars.c clamz.h \ README COPYING clamz.1 configure install-sh \ configure.ac Makefile.in config.h.in \ clamz.desktop clamz.xml @@ -39,8 +39,8 @@ ## Building clamz ## -clamz@EXEEXT@: clamz.@OBJEXT@ options.@OBJEXT@ playlist.@OBJEXT@ download.@OBJEXT@ - $(link) -o clamz@EXEEXT@ clamz.@OBJEXT@ options.@OBJEXT@ playlist.@OBJEXT@ download.@OBJEXT@ $(LIBGCRYPT_LIBS) $(LIBCURL_LIBS) $(LIBS) +clamz@EXEEXT@: clamz.@OBJEXT@ options.@OBJEXT@ playlist.@OBJEXT@ download.@OBJEXT@ vars.@OBJEXT@ + $(link) -o clamz@EXEEXT@ clamz.@OBJEXT@ options.@OBJEXT@ playlist.@OBJEXT@ download.@OBJEXT@ vars.@OBJEXT@ $(LIBGCRYPT_LIBS) $(LIBCURL_LIBS) $(LIBS) clamz.@OBJEXT@: clamz.c clamz.h config.h $(compile) -c $(srcdir)/clamz.c @@ -54,6 +54,9 @@ download.@OBJEXT@: download.c clamz.h config.h $(compile) -c $(srcdir)/download.c +vars.@OBJEXT@: vars.c clamz.h config.h + $(compile) -c $(srcdir)/vars.c + ## Installation ## install: install-clamz install-desktop install-mime @@ -105,7 +108,7 @@ clean: rm -f clamz@EXEEXT@ - rm -f clamz.@OBJEXT@ options.@OBJEXT@ playlist.@OBJEXT@ download.@OBJEXT@ + rm -f clamz.@OBJEXT@ options.@OBJEXT@ playlist.@OBJEXT@ download.@OBJEXT@ vars.@OBJEXT@ distclean: clean rm -rf $(distname) diff -Nru clamz-0.3/options.c clamz-0.4/options.c --- clamz-0.3/options.c 2010-04-01 04:38:27.000000000 +0100 +++ clamz-0.4/options.c 2010-04-19 00:28:51.000000000 +0100 @@ -28,15 +28,25 @@ #include "clamz.h" -char *get_config_file_name(const char *name) +char *get_config_file_name(const char *subdir, const char *base, + const char *suffix) { char *home = getenv("HOME"); char *s; + int n; - if (!home) + if (!home) { + print_error("$HOME not defined"); return NULL; + } + + n = strlen(home) + strlen("/.clamz/") + strlen(base) + 1; + if (subdir) + n += strlen(subdir) + 1; + if (suffix) + n += strlen(suffix); - s = malloc((strlen(home) + strlen(name) + 9) * sizeof(char)); + s = malloc(n * sizeof(char)); if (!s) { print_error("Out of memory"); return NULL; @@ -46,8 +56,17 @@ strcat(s, "/.clamz"); mkdir(s, 0775); + if (subdir) { + strcat(s, "/"); + strcat(s, subdir); + mkdir(s, 0775); + } + strcat(s, "/"); - strcat(s, name); + strcat(s, base); + if (suffix) + strcat(s, suffix); + return s; } @@ -192,7 +211,7 @@ int linenum = 0; char *p; - cfgname = get_config_file_name("config"); + cfgname = get_config_file_name(NULL, "config", NULL); if (!cfgname) return 1; diff -Nru clamz-0.3/playlist.c clamz-0.4/playlist.c --- clamz-0.3/playlist.c 2010-04-01 01:42:05.000000000 +0100 +++ clamz-0.4/playlist.c 2010-04-20 00:01:59.000000000 +0100 @@ -550,44 +550,24 @@ int write_backup_file(const char* b64data, unsigned long b64len, const char* fname) { - char *p = get_config_file_name("amzfiles"); - char *q; - const char *b; - int n; + char *name; FILE *f; - while ((b = strchr(fname, '/'))) - fname = b + 1; + name = get_config_file_name("amzfiles", fname, NULL); - if (!p) { + if (!name) { print_error("Unable to open configuration directory"); return 1; } - mkdir(p, 0775); - - n = strlen(p); - q = malloc((n + strlen(fname) + 2) * sizeof(char)); - if (!q) { - print_error("Out of memory"); - free(p); - return 1; - } - - strcpy(q, p); - strcat(q, "/"); - strcat(q, fname); - - f = fopen(q, "wb"); + f = fopen(name, "wb"); if (!f) { - perror(q); - free(p); - free(q); + perror(name); + free(name); return 1; } - free(p); - free(q); + free(name); if (fwrite(b64data, 1, b64len, f) < b64len) { print_error("Unable to write backup file"); diff -Nru clamz-0.3/README clamz-0.4/README --- clamz-0.3/README 2010-04-01 04:39:31.000000000 +0100 +++ clamz-0.4/README 2010-05-01 03:42:33.000000000 +0100 @@ -1,4 +1,4 @@ -Clamz 0.3 +Clamz 0.4 --------- Clamz is a little command-line program to download MP3 files from @@ -77,11 +77,16 @@ Let's hope there aren't any. If you do encounter a problem, here's what to do: - - KEEP A SECOND COPY OF THE AMZ FILE. Watch out -- if you try to - use Amazon's official MP3 Downloader to open the AMZ file, you'll - find it automatically deletes the file after reading it! Make - sure to keep an extra copy around -- it will be useful in figuring - out what went wrong. + - Keep a second copy of the AMZ file. Clamz will automatically save + a copy in the ~/.clamz/amzfiles/ directory. Be aware, however, + that if you try to use Amazon's official MP3 Downloader to open + the AMZ file, it will automatically delete the file after reading + it! Make sure to keep an extra copy around -- it will be useful + in figuring out what went wrong. + + - Clamz keeps detailed logs of its interactions with the Amazon web + servers; these are stored in the ~/.clamz/logs/ directory. These + log files may also be useful in figuring out what went wrong. - Use clamz -i to print out the details of the AMZ file, if possible. There are basically two ways Amazon might try to break @@ -91,13 +96,43 @@ able to download the files by other means, such as through a web browser. - - Please report any problems you encounter to me at - floppusmaximus@users.sf.net. + - Please report any problems you encounter. You can use the Clamz + bug tracker (http://code.google.com/p/clamz/issues/list), or + contact me directly at floppusmaximus@users.sf.net. History ------- +Version 0.4 -- 2010-04-30 + + * Changed the User-Agent string, to accomodate Amazon's UK servers. + Clamz should work fine with the Amazon UK store, but as I + personally have no way of testing it, your bug reports are always + appreciated! + + * Interactions with the server are recorded to a log file, stored in + the ~/.clamz/logs/ directory. + + * When clamz is launched directly from a web browser, files are + stored in the desktop "music" directory by default. (This is + normally defined in the file ~/.config/user-dirs.dirs if you are + running a desktop environment such as GNOME or KDE. If not, + $HOME/Music is used instead.) + + * File names may include the user-dirs variables ($XDG_DESKTOP_DIR, + $XDG_MUSIC_DIR, etc.), and may include conditional variable + expressions (${foo:-bar} and ${foo:+bar}), similar to shell + variable expansion. + + * Improved curl error reporting. + + * When resuming a partial download, the progress display takes the + existing portion of the file into account. + + * Non-ASCII characters are handled better in the progress display. + + Version 0.3 -- 2010-03-31 * Added MIME info and desktop files for GNOME/KDE/etc. Your web diff -Nru clamz-0.3/vars.c clamz-0.4/vars.c --- clamz-0.3/vars.c 1970-01-01 01:00:00.000000000 +0100 +++ clamz-0.4/vars.c 2010-04-22 18:23:31.000000000 +0100 @@ -0,0 +1,303 @@ +/* + * clamz - Command-line downloader for the Amazon.com MP3 store + * Copyright (c) 2008-2010 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "clamz.h" + +/* Convert a string according to user's preferences */ +static char *convert_string(const clamz_config *cfg, const char *s) +{ + const unsigned char *us = (const unsigned char*) s; + char *converted; + int len; + int i, j; + + len = strlen(s) + 1; + converted = malloc(len * sizeof(char)); + + if (!converted) { + print_error("Out of memory"); + return NULL; + } + + i = j = 0; + while (s[i]) { + if (j >= len - 1) { + len *= 2; + converted = realloc(converted, len * sizeof(char)); + + if (!converted) { + print_error("Out of memory"); + return NULL; + } + } + + if (us[i] & 0x80) { + if (cfg->allowutf8) { + converted[j++] = s[i++]; + } + else { + converted[j++] = '_'; + do + i++; + while (((unsigned char) s[i] & 0xc0) == 0x80); + } + } + else if (s[i] == '/' || iscntrl(us[i])) { + converted[j++] = '_'; + i++; + } + else if (isupper(us[i]) && !cfg->allowupper) { + converted[j++] = tolower(s[i++]); + } + else if (cfg->forbid_chars && strchr(cfg->forbid_chars, s[i])) { + converted[j++] = '_'; + i++; + } + else { + converted[j++] = s[i++]; + } + } + + converted[j] = 0; + + return converted; +} + +/* Get value of an environment variable or special format variable. */ +static char *get_file_var(const clamz_config *cfg, const clamz_track *tr, + const char *var, int use_fallback) +{ + const char *s; + const char *fallback = "Unknown"; + char nbuf[3]; + char *value; + int subst_raw = 0; + + if (!strcasecmp(var, "title")) + s = tr->title; + else if (!strcasecmp(var, "creator")) + s = tr->creator; + else if (!strcasecmp(var, "album")) + s = tr->album; + else if (!strcasecmp(var, "tracknum")) { + s = tr->trackNum; + if (s && s[0] && !s[1]) { + nbuf[0] = '0'; + nbuf[1] = s[0]; + nbuf[2] = 0; + s = nbuf; + } + fallback = "00"; + } + else if (!strcasecmp(var, "album_artist")) + s = find_meta(tr->meta, TMETA_ALBUM_ARTIST); + else if (!strcasecmp(var, "genre")) + s = find_meta(tr->meta, TMETA_GENRE); + else if (!strcasecmp(var, "discnum")) { + s = find_meta(tr->meta, TMETA_DISC_NUM); + fallback = "1"; + } + else if (!strcasecmp(var, "suffix")) { + s = find_meta(tr->meta, TMETA_TRACK_TYPE); + fallback = "mp3"; + } + else if (!strcasecmp(var, "asin")) + s = find_meta(tr->meta, TMETA_ASIN); + else if (!strcasecmp(var, "album_asin")) + s = find_meta(tr->meta, TMETA_ALBUM_ASIN); + else if (!strcasecmp(var, "amz_title")) + s = tr->playlist->title; + else if (!strcasecmp(var, "amz_creator")) + s = tr->playlist->creator; + else if (!strcasecmp(var, "amz_asin")) + s = find_meta(tr->playlist->meta, PMETA_ASIN); + else if (!strcasecmp(var, "amz_genre")) + s = find_meta(tr->playlist->meta, PMETA_GENRE); + else { + s = getenv(var); + fallback = ""; + subst_raw = 1; + } + + if (!s || !s[0]) { + if (use_fallback) + s = fallback; + else + s = ""; + } + + if (subst_raw) { + value = strdup(s); + if (!value) + print_error("Out of memory"); + return value; + } + else { + return convert_string(cfg, s); + } +} + +/* Concatenate value of a variable onto a filename. Add an extra '_' + if necessary to avoid starting a file/directory name with a dot. */ +static int concatenate_var(char **filename, const char *s) +{ + if (s[0] == '.' + && (!*filename || !(*filename)[0] + || (*filename)[strlen(*filename) - 1] == '/')) { + if (concatenate(filename, "_", 1)) + return 1; + } + + return concatenate(filename, s, strlen(s)); +} + +/* Expand a variable reference and append to filename. */ +static int expand_file_var(const clamz_config *cfg, const clamz_track *tr, + char **filename, char *var, const char *format) +{ + char *p, *value; + + if ((p = strchr(var, ':'))) { + if (p[1] == '-') { + /* ${VAR:-ALT} -- substitute VAR if defined, otherwise ALT */ + *p = 0; + if (!(value = get_file_var(cfg, tr, var, 0))) + return 1; + + if (value[0]) { + if (concatenate_var(filename, value)) { + free(value); + return 1; + } + + free(value); + return 0; + } + else { + free(value); + return expand_file_name(cfg, tr, filename, p + 2); + } + } + else if (p[1] == '+') { + /* ${VAR:+ALT} -- substitute ALT if VAR is defined */ + *p = 0; + if (!(value = get_file_var(cfg, tr, var, 0))) + return 1; + + if (value[0]) { + if (expand_file_name(cfg, tr, filename, p + 2)) { + free(value); + return 1; + } + } + + free(value); + return 0; + } + else { + print_error("Invalid expression '${%s}' in '%s'", var, format); + return 1; + } + } + else { + if (!(value = get_file_var(cfg, tr, var, 1))) + return 1; + + if (concatenate_var(filename, value)) { + free(value); + return 1; + } + + free(value); + return 0; + } +} + +/* Expand variable references in a filename format string, and append + result to filename. */ +int expand_file_name(const clamz_config *cfg, const clamz_track *tr, + char **filename, const char *format) +{ + const char *p, *q; + const char *f = format; + char *var; + int n; + + while (*f) { + if ((p = strchr(f, '$'))) { + if (p != f + && concatenate(filename, f, p - f)) + return 1; + + p++; + if (*p == '{') { + p++; + q = p; + n = 1; + while (*q && n) { + if (*q == '{') + n++; + else if (*q == '}') + n--; + q++; + } + if (n) { + print_error("Missing '}' in '%s'", format); + return 1; + } + q--; + f = q + 1; + } + else { + q = p; + while (isalnum((int) (unsigned char) *q) || *q == '_') + q++; + f = q; + } + + if (p == q) { + if (concatenate(filename, "$", 1)) + return 1; + } + else { + var = NULL; + if (concatenate(&var, p, q - p)) + return 1; + if (expand_file_var(cfg, tr, filename, var, format)) { + free(var); + return 1; + } + free(var); + } + } + else { + return concatenate(filename, f, strlen(f)); + } + } + + return 0; +}