1#!/usr/bin/python3 2# Copyright 2015 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6""" 7Mail the content of standard input. 8 9Example usage: 10 Use pipe: 11 $ echo "Some content" |./gmail_lib.py -s "subject" [email protected] [email protected] 12 13 Manually input: 14 $ ./gmail_lib.py -s "subject" [email protected] [email protected] 15 > Line 1 16 > Line 2 17 Ctrl-D to end standard input. 18""" 19from __future__ import absolute_import 20from __future__ import division 21from __future__ import print_function 22 23import argparse 24import base64 25import httplib2 26import logging 27import sys 28import random 29from email.mime.text import MIMEText 30 31import common 32from autotest_lib.server import site_utils 33 34try: 35 from apiclient.discovery import build as apiclient_build 36 from apiclient import errors as apiclient_errors 37 from oauth2client import file as oauth_client_fileio 38except ImportError as e: 39 apiclient_build = None 40 logging.debug("API client for gmail disabled. %s", e) 41 42 43RETRY_DELAY = 5 44RETRY_BACKOFF_FACTOR = 1.5 45MAX_RETRY = 10 46RETRIABLE_MSGS = [ 47 # User-rate limit exceeded 48 r'HttpError 429',] 49 50class GmailApiException(Exception): 51 """Exception raised in accessing Gmail API.""" 52 53 54class Message(): 55 """An email message.""" 56 57 def __init__(self, to, subject, message_text): 58 """Initialize a message. 59 60 @param to: The recievers saperated by comma. 61 e.g. '[email protected],[email protected]' 62 @param subject: String, subject of the message 63 @param message_text: String, content of the message. 64 """ 65 self.to = to 66 self.subject = subject 67 self.message_text = message_text 68 69 70 def get_payload(self): 71 """Get the payload that can be sent to the Gmail API. 72 73 @return: A dictionary representing the message. 74 """ 75 message = MIMEText(self.message_text) 76 message['to'] = self.to 77 message['subject'] = self.subject 78 return {'raw': base64.urlsafe_b64encode(message.as_string())} 79 80 81class GmailApiClient(): 82 """Client that talks to Gmail API.""" 83 84 def __init__(self, oauth_credentials): 85 """Init Gmail API client 86 87 @param oauth_credentials: Path to the oauth credential token. 88 """ 89 if not apiclient_build: 90 raise GmailApiException('Cannot get apiclient library.') 91 92 storage = oauth_client_fileio.Storage(oauth_credentials) 93 credentials = storage.get() 94 if not credentials or credentials.invalid: 95 raise GmailApiException('Invalid credentials for Gmail API, ' 96 'could not send email.') 97 http = credentials.authorize(httplib2.Http()) 98 self._service = apiclient_build('gmail', 'v1', http=http) 99 100 101 def send_message(self, message, ignore_error=True): 102 """Send an email message. 103 104 @param message: Message to be sent. 105 @param ignore_error: If True, will ignore any HttpError. 106 """ 107 try: 108 # 'me' represents the default authorized user. 109 message = self._service.users().messages().send( 110 userId='me', body=message.get_payload()).execute() 111 logging.debug('Email sent: %s' , message['id']) 112 except apiclient_errors.HttpError as error: 113 if ignore_error: 114 logging.error('Failed to send email: %s', error) 115 else: 116 raise 117 118 119def send_email(to, subject, message_text, retry=True, creds_path=None): 120 """Send email. 121 122 @param to: The recipients, separated by comma. 123 @param subject: Subject of the email. 124 @param message_text: Text to send. 125 @param retry: If retry on retriable failures as defined in RETRIABLE_MSGS. 126 @param creds_path: The credential path for gmail account, if None, 127 will use DEFAULT_CREDS_FILE. 128 """ 129 # TODO(ayatane): Deprecated, not untangling imports now 130 pass 131 132 133if __name__ == '__main__': 134 logging.basicConfig(level=logging.DEBUG) 135 parser = argparse.ArgumentParser( 136 description=__doc__, formatter_class=argparse.RawTextHelpFormatter) 137 parser.add_argument('-s', '--subject', type=str, dest='subject', 138 required=True, help='Subject of the mail') 139 parser.add_argument('-p', type=float, dest='probability', 140 required=False, default=0, 141 help='(optional) per-addressee probability ' 142 'with which to send email. If not specified ' 143 'all addressees will receive message.') 144 parser.add_argument('recipients', nargs='*', 145 help='Email addresses separated by space.') 146 args = parser.parse_args() 147 if not args.recipients or not args.subject: 148 print('Requires both recipients and subject.') 149 sys.exit(1) 150 151 message_text = sys.stdin.read() 152 153 if args.probability: 154 recipients = [] 155 for r in args.recipients: 156 if random.random() < args.probability: 157 recipients.append(r) 158 if recipients: 159 print('Randomly selected recipients %s' % recipients) 160 else: 161 print('Random filtering removed all recipients. Sending nothing.') 162 sys.exit(0) 163 else: 164 recipients = args.recipients 165 166 167 with site_utils.SetupTsMonGlobalState('gmail_lib', short_lived=True): 168 send_email(','.join(recipients), args.subject , message_text) 169