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