blob: fef44ad3f347a1cf65af73b91264823dce1942fc [file] [log] [blame]
mdm@chromium.org56df6d32010-08-31 18:16:49 +00001#!/bin/sh
dkegel@google.comf20da1f2009-06-30 19:16:32 +00002#---------------------------------------------
3# xdg-email
4#
5# Utility script to open the users favorite email program, using the
6# RFC 2368 mailto: URI spec
7#
8# Refer to the usage() function below for usage.
9#
10# Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
11# Copyright 2006, Jeremy White <jwhite@codeweavers.com>
12#
13# LICENSE:
14#
15# Permission is hereby granted, free of charge, to any person obtaining a
16# copy of this software and associated documentation files (the "Software"),
17# to deal in the Software without restriction, including without limitation
18# the rights to use, copy, modify, merge, publish, distribute, sublicense,
19# and/or sell copies of the Software, and to permit persons to whom the
20# Software is furnished to do so, subject to the following conditions:
21#
22# The above copyright notice and this permission notice shall be included
23# in all copies or substantial portions of the Software.
24#
25# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
31# OTHER DEALINGS IN THE SOFTWARE.
32#
33#---------------------------------------------
34
35manualpage()
36{
37cat << _MANUALPAGE
38Name
39
40xdg-email - command line tool for sending mail using the user's preferred
41e-mail composer
42
43Synopsis
44
45xdg-email [--utf8] [--cc address] [--bcc address] [--subject text] [--body text
46] [--attach file] [ mailto-uri | address(es) ]
47
48xdg-email { --help | --manual | --version }
49
50Description
51
52xdg-email opens the user's preferred e-mail composer in order to send a mail to
53address(es) or mailto-uri. RFC2368 defines mailto: URIs. xdg-email limits
54support to, cc, subject and body fields in mailto-uri, all other fields are
55silently ignored. address(es) must follow the syntax of RFC822. Multiple
56addresses may be provided as separate arguments.
57
58All information provided on the command line is used to prefill corresponding
59fields in the user's e-mail composer. The user will have the opportunity to
60change any of this information before actually sending the e-mail.
61
62xdg-email is for use inside a desktop session only. It is not recommended to
63use xdg-email as root.
64
65See http://portland.freedesktop.org/EmailConfig for information on how the user
66can change the e-mail composer that is used.
67
68Options
69
70--utf8
71 Indicates that all command line options that follow are in utf8. Without
72 this option, command line options are expected to be encoded according to
73 locale. If the locale already specifies utf8 this option has no effect.
74 This option does not affect mailto URIs that are passed on the command
75 line.
76--cc address
77 Specify a recipient to be copied on the e-mail.
78--bcc address
79 Specify a recipient to be blindly copied on the e-mail.
80--subject text
81 Specify a subject for the e-mail.
82--body text
83 Specify a body for the e-mail. Since the user will be able to make changes
84 before actually sending the e-mail, this can be used to provide the user
85 with a template for the e-mail. text may contain linebreaks.
86--attach file
87
88 Specify an attachment for the e-mail. file must point to an existing file.
89
90 Some e-mail applications require the file to remain present after xdg-email
91 returns.
92
93--help
94 Show command synopsis.
95--manual
96 Show this manualpage.
97--version
98 Show the xdg-utils version information.
99
100Environment Variables
101
102xdg-email honours the following environment variables:
103
104XDG_UTILS_DEBUG_LEVEL
105 Setting this environment variable to a non-zero numerical value makes
106 xdg-email do more verbose reporting on stderr. Setting a higher value
107 increases the verbosity.
108
109Exit Codes
110
111An exit code of 0 indicates success while a non-zero exit code indicates
112failure. The following failure codes can be returned:
113
1141
115 Error in command line syntax.
1162
117 One of the files passed on the command line did not exist.
1183
119 A required tool could not be found.
1204
121 The action failed.
1225
123 No permission to read one of the files passed on the command line.
124
125Configuration
126
127Visit http://portland.freedesktop.org/EmailConfig for information how to
128configure xdg-email to use the email client of your choice.
129
130Examples
131
132xdg-email 'Jeremy White <jwhite@example.com>'
133
134xdg-email --attach /tmp/logo.png \
135 --subject 'Logo contest' \
136 --body 'Attached you find the logo for the contest.' \
137 'jwhite@example.com'
138
139xdg-email --subject 'Your password is about to expire' \
140 'jwhite@example.com' 'bastian@example.com' 'whipple@example.com'
141
142_MANUALPAGE
143}
144
145usage()
146{
147cat << _USAGE
148xdg-email - command line tool for sending mail using the user's preferred
149e-mail composer
150
151Synopsis
152
153xdg-email [--utf8] [--cc address] [--bcc address] [--subject text] [--body text
154] [--attach file] [ mailto-uri | address(es) ]
155
156xdg-email { --help | --manual | --version }
157
158_USAGE
159}
160
161#@xdg-utils-common@
162
163#----------------------------------------------------------------------------
164# Common utility functions included in all XDG wrapper scripts
165#----------------------------------------------------------------------------
166
167DEBUG()
168{
169 [ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
170 [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
171 shift
172 echo "$@" >&2
173}
174
175#-------------------------------------------------------------
176# Exit script on successfully completing the desired operation
177
178exit_success()
179{
180 if [ $# -gt 0 ]; then
181 echo "$@"
182 echo
183 fi
184
185 exit 0
186}
187
188
189#-----------------------------------------
190# Exit script on malformed arguments, not enough arguments
191# or missing required option.
192# prints usage information
193
194exit_failure_syntax()
195{
196 if [ $# -gt 0 ]; then
197 echo "xdg-email: $@" >&2
198 echo "Try 'xdg-email --help' for more information." >&2
199 else
200 usage
201 echo "Use 'man xdg-email' or 'xdg-email --manual' for additional info."
202 fi
203
204 exit 1
205}
206
207#-------------------------------------------------------------
208# Exit script on missing file specified on command line
209
210exit_failure_file_missing()
211{
212 if [ $# -gt 0 ]; then
213 echo "xdg-email: $@" >&2
214 fi
215
216 exit 2
217}
218
219#-------------------------------------------------------------
220# Exit script on failure to locate necessary tool applications
221
222exit_failure_operation_impossible()
223{
224 if [ $# -gt 0 ]; then
225 echo "xdg-email: $@" >&2
226 fi
227
228 exit 3
229}
230
231#-------------------------------------------------------------
232# Exit script on failure returned by a tool application
233
234exit_failure_operation_failed()
235{
236 if [ $# -gt 0 ]; then
237 echo "xdg-email: $@" >&2
238 fi
239
240 exit 4
241}
242
243#------------------------------------------------------------
244# Exit script on insufficient permission to read a specified file
245
246exit_failure_file_permission_read()
247{
248 if [ $# -gt 0 ]; then
249 echo "xdg-email: $@" >&2
250 fi
251
252 exit 5
253}
254
255#------------------------------------------------------------
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000256# Exit script on insufficient permission to write a specified file
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000257
258exit_failure_file_permission_write()
259{
260 if [ $# -gt 0 ]; then
261 echo "xdg-email: $@" >&2
262 fi
263
264 exit 6
265}
266
267check_input_file()
268{
269 if [ ! -e "$1" ]; then
270 exit_failure_file_missing "file '$1' does not exist"
271 fi
272 if [ ! -r "$1" ]; then
273 exit_failure_file_permission_read "no permission to read file '$1'"
274 fi
275}
276
277check_vendor_prefix()
278{
279 file_label="$2"
280 [ -n "$file_label" ] || file_label="filename"
281 file=`basename "$1"`
282 case "$file" in
283 [a-zA-Z]*-*)
284 return
285 ;;
286 esac
287
288 echo "xdg-email: $file_label '$file' does not have a proper vendor prefix" >&2
289 echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
290 echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
291 echo "Use --novendor to override or 'xdg-email --manual' for additional info." >&2
292 exit 1
293}
294
295check_output_file()
296{
297 # if the file exists, check if it is writeable
298 # if it does not exists, check if we are allowed to write on the directory
299 if [ -e "$1" ]; then
300 if [ ! -w "$1" ]; then
301 exit_failure_file_permission_write "no permission to write to file '$1'"
302 fi
303 else
304 DIR=`dirname "$1"`
305 if [ ! -w "$DIR" -o ! -x "$DIR" ]; then
306 exit_failure_file_permission_write "no permission to create file '$1'"
307 fi
308 fi
309}
310
311#----------------------------------------
312# Checks for shared commands, e.g. --help
313
314check_common_commands()
315{
316 while [ $# -gt 0 ] ; do
317 parm="$1"
318 shift
319
320 case "$parm" in
321 --help)
322 usage
323 echo "Use 'man xdg-email' or 'xdg-email --manual' for additional info."
324 exit_success
325 ;;
326
327 --manual)
328 manualpage
329 exit_success
330 ;;
331
332 --version)
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000333 echo "xdg-email 1.0.2"
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000334 exit_success
335 ;;
336 esac
337 done
338}
339
340check_common_commands "$@"
341
342[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
343if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
344 # Be silent
345 xdg_redirect_output=" > /dev/null 2> /dev/null"
346else
347 # All output to stderr
348 xdg_redirect_output=" >&2"
349fi
350
351#--------------------------------------
352# Checks for known desktop environments
353# set variable DE to the desktop environments name, lowercase
354
355detectDE()
356{
357 if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
358 elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000359 elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
360 elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000361 fi
362}
363
364#----------------------------------------------------------------------------
365# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
366# It also always returns 1 in KDE 3.4 and earlier
367# Simply return 0 in such case
368
369kfmclient_fix_exit_code()
370{
371 version=`kde${KDE_SESSION_VERSION}-config --version 2>/dev/null | grep KDE`
372 major=`echo $version | sed 's/KDE: \([0-9]\).*/\1/'`
373 minor=`echo $version | sed 's/KDE: [0-9]*\.\([0-9]\).*/\1/'`
374 release=`echo $version | sed 's/KDE: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
375 test "$major" -gt 3 && return $1
376 test "$minor" -gt 5 && return $1
377 test "$release" -gt 4 && return $1
378 return 0
379}
380
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000381run_thunderbird()
382{
383 local THUNDERBIRD MAILTO NEWMAILTO TO CC BCC SUBJECT BODY ATTACH
384 THUNDERBIRD="$1"
385 MAILTO=$(echo "$2" | sed 's/^mailto://')
386 echo "$MAILTO" | grep -qs "^?"
387 if [ "$?" = "0" ] ; then
388 MAILTO=$(echo "$MAILTO" | sed 's/^?//')
389 else
390 MAILTO=$(echo "$MAILTO" | sed 's/^/to=/' | sed 's/?/\&/')
391 fi
392
393 MAILTO=$(echo "$MAILTO" | sed 's/&/\n/g')
394 TO=$(echo "$MAILTO" | grep '^to=' | sed 's/^to=//' | awk '{ printf "%s,",$0 }')
395 CC=$(echo "$MAILTO" | grep '^cc=' | sed 's/^cc=//' | awk '{ printf "%s,",$0 }')
396 BCC=$(echo "$MAILTO" | grep '^bcc=' | sed 's/^bcc=//' | awk '{ printf "%s,",$0 }')
397 SUBJECT=$(echo "$MAILTO" | grep '^subject=' | tail -n 1)
398 BODY=$(echo "$MAILTO" | grep '^body=' | tail -n 1)
399 ATTACH=$(echo "$MAILTO" | sed 's/^attach=/\n\nfile:\/\//g' | awk '/^file:/ { printf "%s,",$0 }')
400
401 if [ -z "$TO" ] ; then
402 NEWMAILTO=
403 else
404 NEWMAILTO="to='$TO'"
405 fi
406 if [ -n "$CC" ] ; then
407 NEWMAILTO="${NEWMAILTO},cc='$CC'"
408 fi
409 if [ -n "$BCC" ] ; then
410 NEWMAILTO="${NEWMAILTO},bcc='$BCC'"
411 fi
412 if [ -n "$SUBJECT" ] ; then
413 NEWMAILTO="${NEWMAILTO},$SUBJECT"
414 fi
415 if [ -n "$BODY" ] ; then
416 NEWMAILTO="${NEWMAILTO},$BODY"
417 fi
418
419 if [ -n "$ATTACH" ] ; then
420 NEWMAILTO="${NEWMAILTO},attachment='${ATTACH}'"
421 fi
422
423 NEWMAILTO=$(echo "$NEWMAILTO" | sed 's/^,//')
424 DEBUG 1 "Running $THUNDERBIRD -compose \"$NEWMAILTO\""
425 "$THUNDERBIRD" -compose "$NEWMAILTO"
426 if [ $? -eq 0 ]; then
427 exit_success
428 else
429 exit_failure_operation_failed
430 fi
431}
432
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000433open_kde()
434{
435 local client
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000436 client=`kreadconfig --file emaildefaults --group PROFILE_Default --key EmailClient | cut -d ' ' -f 1`
437 echo $client | grep thunderbird > /dev/null 2>&1
438 if [ $? -eq 0 ] ; then
439 run_thunderbird "$client" "$1"
440 fi
441
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000442 if [ -f /etc/SuSE-release ] ; then
443 # Workaround for SUSE 10.0
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000444 [ -z "$client" ] && client="kmail"
445 if ! which "$client" > /dev/null 2> /dev/null; then
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000446 DEBUG 3 "KDE has $client configured as email client which isn't installed"
447 if which gnome-open > /dev/null 2> /dev/null && which evolution > /dev/null 2> /dev/null; then
448 DEBUG 3 "Try gnome-open instead"
449 open_gnome "$1"
450 fi
451 fi
452 fi
453 DEBUG 1 "Running kmailservice \"$1\""
mdm@chromium.orgad856b92009-07-27 21:50:25 +0000454 if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
455 KMAILSERVICE=`kde4-config --locate kmailservice --path exe 2>/dev/null`
456 else
457 KMAILSERVICE=`which kmailservice 2>/dev/null`
458 fi
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000459 # KDE uses locale's encoding when decoding the URI, so set it to UTF-8
mdm@chromium.orgad856b92009-07-27 21:50:25 +0000460 LC_ALL=C.UTF-8 $KMAILSERVICE "$1"
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000461 kfmclient_fix_exit_code $?
462
463 if [ $? -eq 0 ]; then
464 exit_success
465 else
466 exit_failure_operation_failed
467 fi
468}
469
470open_gnome()
471{
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000472 local client
473 client=`gconftool-2 --get /desktop/gnome/url-handlers/mailto/command | cut -d ' ' -f 1` || ""
474 echo $client | grep thunderbird > /dev/null 2>&1
475 if [ $? -eq 0 ] ; then
476 run_thunderbird "$client" "$1"
477 fi
478
479 if gvfs-open --help 2>/dev/null 1>&2; then
480 DEBUG 1 "Running gvfs-open \"$1\""
481 gvfs-open "$1"
482 else
483 DEBUG 1 "Running gnome-open \"$1\""
484 gnome-open "$1"
485 fi
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000486
487 if [ $? -eq 0 ]; then
488 exit_success
489 else
490 exit_failure_operation_failed
491 fi
492}
493
494
495open_xfce()
496{
497 DEBUG 1 "Running exo-open \"$1\""
498 exo-open "$1"
499
500 if [ $? -eq 0 ]; then
501 exit_success
502 else
503 exit_failure_operation_failed
504 fi
505}
506
507open_generic()
508{
509 IFS=":"
510 for browser in $BROWSER; do
511 if [ x"$browser" != x"" ]; then
512
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000513 browser_with_arg=`printf "$browser" "$1" 2>/dev/null`
514 if [ $? -ne 0 ]; then browser_with_arg=$browser;
515 fi
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000516
517 if [ x"$browser_with_arg" = x"$browser" ]; then "$browser" "$1";
518 else $browser_with_arg;
519 fi
520
521 if [ $? -eq 0 ]; then exit_success;
522 fi
523 fi
524 done
525
526 exit_failure_operation_impossible "no method available for opening '$1'"
527}
528
529url_encode()
530{
531result=$(echo "$1" | $utf8 | awk '
532 BEGIN {
533 for ( i=1; i<=255; ++i ) ord [ sprintf ("%c", i) "" ] = i + 0
534 e = ""
535 linenr = 1
536 }
537 {
538 if ( linenr++ != 1 ) {
539 e = e "%0D%0A"
540 }
541 for ( i=1; i<=length ($0); ++i ) {
542 c = substr ($0, i, 1)
543 if ( ord [c] > 127 ) {
544 e = e "%" sprintf("%02X", ord [c])
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000545 } else if ( c ~ /[@a-zA-Z0-9.-\\/]/ ) {
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000546 e = e c
547 } else {
548 e = e "%" sprintf("%02X", ord [c])
549 }
550 }
551 }
552 END {
553 print e
554 }
555')
556}
557
558options=
559mailto=
560utf8="iconv -t utf8"
561while [ $# -gt 0 ] ; do
562 parm="$1"
563 shift
564
565 case "$parm" in
566 --utf8)
567 utf8="cat"
568 ;;
569
570 --to)
571 if [ -z "$1" ] ; then
572 exit_failure_syntax "email address argument missing for --to"
573 fi
574 url_encode "$1"
575 options="${options}to=${result}&"
576 shift
577 ;;
578
579 --cc)
580 if [ -z "$1" ] ; then
581 exit_failure_syntax "email address argument missing for --cc"
582 fi
583 url_encode "$1"
584 options="${options}cc=${result}&"
585 shift
586 ;;
587
588 --bcc)
589 if [ -z "$1" ] ; then
590 exit_failure_syntax "email address argument missing for --bcc"
591 fi
592 url_encode "$1"
593 options="${options}bcc=${result}&"
594 shift
595 ;;
596
597 --subject)
598 if [ -z "$1" ] ; then
599 exit_failure_syntax "text argument missing for --subject option"
600 fi
601 url_encode "$1"
602 options="${options}subject=${result}&"
603 shift
604 ;;
605
606 --body)
607 if [ -z "$1" ] ; then
608 exit_failure_syntax "text argument missing for --body option"
609 fi
610 url_encode "$1"
611 options="${options}body=${result}&"
612 shift
613 ;;
614
615 --attach)
616 if [ -z "$1" ] ; then
617 exit_failure_syntax "file argument missing for --attach option"
618 fi
619 check_input_file "$1"
620 file=`readlink -f "$1"` # Normalize path
621 if [ -z "$file" -o ! -f "$file" ] ; then
622 exit_failure_file_missing "file '$1' does not exist"
623 fi
624
625 url_encode "$file"
626 options="${options}attach=${result}&"
627 shift
628 ;;
629
630 -*)
631 exit_failure_syntax "unexpected option '$parm'"
632 ;;
633
634 mailto:*)
635 mailto="$parm"
636 ;;
637
638 *@*)
639 url_encode "$parm"
640 if [ -z "${mailto}" ] ; then
641 mailto="mailto:"${result}"?"
642 else
643 options="${options}to=${result}&"
644 fi
645 ;;
646
647 *)
648 exit_failure_syntax "unexpected argument '$parm'"
649 ;;
650 esac
651done
652
653if [ -z "${mailto}" ] ; then
654 # TO address is optional
655 mailto="mailto:?"
656fi
657
658case $mailto in
659 *\?)
660 mailto="${mailto}${options}"
661 ;;
662
663 *\?*)
664 mailto="${mailto}&${options}"
665 ;;
666
667 *)
668 mailto="${mailto}?${options}"
669 ;;
670esac
671
672# Strip trailing ? and &
673mailto=`echo "${mailto}"| sed 's/[?&]$//'`
674
675# Shouldn't happen
676[ x"${mailto}" != x"" ] || exit_failure_syntax
677
678if which xdg-email-hook.sh > /dev/null 2> /dev/null; then
679 xdg-email-hook.sh "${mailto}"
680 if [ $? -eq 0 ]; then
681 exit_success
682 else
683 exit_failure_operation_failed
684 fi
685fi
686
687detectDE
688
689if [ x"$DE" = x"" ]; then
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000690 DE=generic
691fi
692
mdm@chromium.org56df6d32010-08-31 18:16:49 +0000693# if BROWSER variable is not set, check some well known browsers instead
694if [ x"$BROWSER" = x"" ]; then
695 BROWSER=links2:links:lynx:w3m
696 if [ -n "$DISPLAY" ]; then
697 BROWSER=firefox:mozilla:epiphany:konqueror:chromium-browser:google-chrome:$BROWSER
698 fi
699fi
700
dkegel@google.comf20da1f2009-06-30 19:16:32 +0000701case "$DE" in
702 kde)
703 open_kde "${mailto}"
704 ;;
705
706 gnome)
707 open_gnome "${mailto}"
708 ;;
709
710 xfce)
711 open_xfce "${mailto}"
712 ;;
713
714 generic)
715 open_generic "${mailto}"
716 ;;
717
718 *)
719 exit_failure_operation_impossible "no method available for opening '${mailto}'"
720 ;;
721esac