|
25 | 25 | """ |
26 | 26 |
|
27 | 27 | __author__ = 'GAM Team <google-apps-manager@googlegroups.com>' |
28 | | -__version__ = '7.36.02' |
| 28 | +__version__ = '7.36.03' |
29 | 29 | __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' |
30 | 30 |
|
31 | 31 | # pylint: disable=wrong-import-position |
@@ -7341,7 +7341,8 @@ def _addEmbeddedImagesToMessage(message, embeddedImages): |
7341 | 7341 | # Send an email |
7342 | 7342 | def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msgFrom=None, msgReplyTo=None, |
7343 | 7343 | html=False, charset=UTF8, attachments=None, embeddedImages=None, |
7344 | | - msgHeaders=None, ccRecipients=None, bccRecipients=None, mailBox=None, threadId=None): |
| 7344 | + msgHeaders=None, ccRecipients=None, bccRecipients=None, mailBox=None, threadId=None, |
| 7345 | + action=Act.SENDEMAIL): |
7345 | 7346 | def checkResult(entityType, recipients): |
7346 | 7347 | if not recipients: |
7347 | 7348 | return |
@@ -7397,12 +7398,13 @@ def cleanAddr(emailAddr): |
7397 | 7398 | if mailBox is None: |
7398 | 7399 | mailBox = msgFromAddr |
7399 | 7400 | _, mailBoxAddr = cleanAddr(mailBox) |
7400 | | - action = Act.Get() |
7401 | | - Act.Set(Act.SENDEMAIL) |
| 7401 | + parentAction = Act.Get() |
| 7402 | + Act.Set(action) |
7402 | 7403 | if not GC.Values[GC.SMTP_HOST]: |
7403 | 7404 | if not clientAccess: |
7404 | 7405 | userId, gmail = buildGAPIServiceObject(API.GMAIL, mailBoxAddr) |
7405 | 7406 | if not gmail: |
| 7407 | + Act.Set(parentAction) |
7406 | 7408 | return |
7407 | 7409 | else: |
7408 | 7410 | userId = mailBoxAddr |
@@ -7444,7 +7446,7 @@ def cleanAddr(emailAddr): |
7444 | 7446 | server.quit() |
7445 | 7447 | except Exception: |
7446 | 7448 | pass |
7447 | | - Act.Set(action) |
| 7449 | + Act.Set(parentAction) |
7448 | 7450 |
|
7449 | 7451 | def addFieldToFieldsList(fieldName, fieldsChoiceMap, fieldsList): |
7450 | 7452 | fields = fieldsChoiceMap[fieldName.lower()] |
@@ -15385,32 +15387,35 @@ def getRecipients(): |
15385 | 15387 | return [normalizeEmailAddressOrUID(emailAddress, noUid=True, noLower=True) for emailAddress in recipients] |
15386 | 15388 | return getNormalizedEmailAddressEntity(shlexSplit=True, noLower=True) |
15387 | 15389 |
|
15388 | | -# gam sendemail [recipient|to] <RecipientEntity> [from <EmailAddress>] [mailbox <EmailAddress>] [replyto <EmailAddress>] |
| 15390 | +# gam sendemail [recipient|to] <RecipientEntity> |
| 15391 | +# [from <EmailAddress>] [mailbox <EmailAddress>] [replyto <EmailAddress>] |
15389 | 15392 | # [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] |
15390 | | -# [subject <String>] [<MessageContent>] |
| 15393 | +# [subject <String>] [<MessageContent>] [html [<Boolean>]] |
15391 | 15394 | # (replace <Tag> <String>)* |
15392 | 15395 | # (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)* |
15393 | | -# [html [<Boolean>]] (attach <FileName> [charset <CharSet>])* |
| 15396 | +# (attach <FileName> [charset <CharSet>])* |
15394 | 15397 | # (embedimage <FileName> <String>)* |
15395 | 15398 | # [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] |
15396 | 15399 | # (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* |
15397 | 15400 | # [threadid <String>] |
15398 | | -# gam <UserTypeEntity> sendemail recipient|to <RecipientEntity> [replyto <EmailAddress>] |
| 15401 | +# gam <UserTypeEntity> sendemail recipient|to <RecipientEntity> |
| 15402 | +# [replyto <EmailAddress>] |
15399 | 15403 | # [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] |
15400 | | -# [subject <String>] [<MessageContent>] |
| 15404 | +# [subject <String>] [<MessageContent>] [html [<Boolean>]] |
15401 | 15405 | # (replace <Tag> <String>)* |
15402 | 15406 | # (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)* |
15403 | | -# [html [<Boolean>]] (attach <FileName> [charset <CharSet>])* |
| 15407 | +# (attach <FileName> [charset <CharSet>])* |
15404 | 15408 | # (embedimage <FileName> <String>)* |
15405 | 15409 | # [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] |
15406 | 15410 | # (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* |
15407 | 15411 | # [threadid <String>] |
15408 | | -# gam <UserTypeEntity> sendemail from <EmailAddress> [replyto <EmailAddress>] |
| 15412 | +# gam <UserTypeEntity> sendemail from <EmailAddress> |
| 15413 | +# [replyto <EmailAddress>] |
15409 | 15414 | # [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage] |
15410 | | -# [subject <String>] [<MessageContent>] |
| 15415 | +# [subject <String>] [<MessageContent> ][html [<Boolean>]] |
15411 | 15416 | # (replace <Tag> <String>)* |
15412 | 15417 | # (replaceregex <REMatchPattern> <RESubstitution> <Tag> <String>)* |
15413 | | -# [html [<Boolean>]] (attach <FileName> [charset <CharSet>])* |
| 15418 | +# (attach <FileName> [charset <CharSet>])* |
15414 | 15419 | # (embedimage <FileName> <String>)* |
15415 | 15420 | # [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>] |
15416 | 15421 | # (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* |
@@ -15531,6 +15536,125 @@ def doSendEmail(users=None): |
15531 | 15536 | attachments=attachments, embeddedImages=embeddedImages, msgHeaders=msgHeaders, mailBox=mailBox, threadId=threadId) |
15532 | 15537 | Ind.Decrement() |
15533 | 15538 |
|
| 15539 | +# gam <UserTypeEntity> sendreply |
| 15540 | +# (((query <QueryGmail> [querytime<String> <Date>]*) [or|and])+) | (ids <MessageIDEntity>) |
| 15541 | +# [replyto <EmailAddress>] |
| 15542 | +# [subject <String>] [<MessageContent>] [html [<Boolean>]] |
| 15543 | +# (attach <FileName> [charset <CharSet>])* |
| 15544 | +# (embedimage <FileName> <String>)* |
| 15545 | +# (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)* |
| 15546 | +def doSendReply(users): |
| 15547 | + def _getHeaderValue(name): |
| 15548 | + for header in messageInfo['payload']['headers']: |
| 15549 | + if name == header['name']: |
| 15550 | + return _decodeHeader(header['value']) |
| 15551 | + return '' |
| 15552 | + |
| 15553 | + notify = {'subject': '', 'message': '', 'html': False, 'charset': UTF8} |
| 15554 | + query = '' |
| 15555 | + queryTimes = {} |
| 15556 | + messageIds = [] |
| 15557 | + msgHeaders = {} |
| 15558 | + msgReplyTo = None |
| 15559 | + attachments = [] |
| 15560 | + embeddedImages = [] |
| 15561 | + while Cmd.ArgumentsRemaining(): |
| 15562 | + myarg = getArgument() |
| 15563 | + if myarg == 'query': |
| 15564 | + selectLocation = Cmd.Location() |
| 15565 | + if query: |
| 15566 | + query += ' ' |
| 15567 | + query += f'({getString(Cmd.OB_QUERY)})' |
| 15568 | + elif myarg.startswith('querytime'): |
| 15569 | + queryTimes[myarg] = getDateOrDeltaFromNow().replace('-', '/') |
| 15570 | + elif myarg in {'or', 'and'}: |
| 15571 | + if query: |
| 15572 | + query += f' {myarg.upper()}' |
| 15573 | + elif myarg == 'ids': |
| 15574 | + selectLocation = Cmd.Location() |
| 15575 | + messageIds = getEntityList(Cmd.OB_MESSAGE_ID) |
| 15576 | + elif myarg == 'subject': |
| 15577 | + notify['subject'] = getString(Cmd.OB_STRING) |
| 15578 | + elif myarg in SORF_MSG_FILE_ARGUMENTS: |
| 15579 | + notify['message'], notify['charset'], notify['html'] = getStringOrFile(myarg) |
| 15580 | + elif myarg == 'replyto': |
| 15581 | + msgReplyTo = getString(Cmd.OB_EMAIL_ADDRESS) |
| 15582 | + elif myarg == 'html': |
| 15583 | + notify['html'] = getBoolean() |
| 15584 | + elif myarg == 'attach': |
| 15585 | + attachments.append((getFilename(), getCharSet())) |
| 15586 | + elif myarg == 'embedimage': |
| 15587 | + embeddedImages.append((getFilename(), getString(Cmd.OB_STRING))) |
| 15588 | + elif myarg in SMTP_HEADERS_MAP: |
| 15589 | + if myarg in SMTP_DATE_HEADERS: |
| 15590 | + msgDate, _, _ = getTimeOrDeltaFromNow(True) |
| 15591 | + msgHeaders[SMTP_HEADERS_MAP[myarg]] = formatdate(time.mktime(msgDate.timetuple()) + msgDate.microsecond/1E6, True) |
| 15592 | + else: |
| 15593 | + msgHeaders[SMTP_HEADERS_MAP[myarg]] = getString(Cmd.OB_STRING) |
| 15594 | + elif myarg == 'header': |
| 15595 | + header = getString(Cmd.OB_STRING, minLen=1) |
| 15596 | + msgHeaders[SMTP_HEADERS_MAP.get(header.lower(), header)] = getString(Cmd.OB_STRING) |
| 15597 | + else: |
| 15598 | + unknownArgumentExit() |
| 15599 | + if query and messageIds: |
| 15600 | + Cmd.SetLocation(selectLocation-1) |
| 15601 | + usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('query <QueryGmail>', 'ids <MessageIDEntity>')) |
| 15602 | + notify['message'] = notify['message'].replace('\r', '').replace('\\n', '\n') |
| 15603 | + i, count, users = getEntityArgument(users) |
| 15604 | + for user in users: |
| 15605 | + i += 1 |
| 15606 | + user, gmail = buildGAPIServiceObject(API.GMAIL, user, i, count) |
| 15607 | + if not gmail: |
| 15608 | + continue |
| 15609 | + try: |
| 15610 | + if query: |
| 15611 | + printGettingAllEntityItemsForWhom(Ent.MESSAGE, user, i, count, query=query) |
| 15612 | + listResult = callGAPIpages(gmail.users().messages(), 'list', 'messages', |
| 15613 | + pageMessage=getPageMessageForWhom(), |
| 15614 | + throwReasons=GAPI.GMAIL_THROW_REASONS+GAPI.GMAIL_LIST_THROW_REASONS, |
| 15615 | + userId='me', q=query, fields='nextPageToken,messages(id)', |
| 15616 | + maxResults=GC.Values[GC.MESSAGE_MAX_RESULTS]) |
| 15617 | + messageIds = [message['id'] for message in listResult] |
| 15618 | + except (GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.invalid, GAPI.invalidArgument) as e: |
| 15619 | + entityActionFailedWarning([Ent.USER, user], str(e), i, count) |
| 15620 | + continue |
| 15621 | + except GAPI.serviceNotAvailable: |
| 15622 | + userGmailServiceNotEnabledWarning(user, i, count) |
| 15623 | + continue |
| 15624 | + jcount = len(messageIds) |
| 15625 | + if jcount == 0: |
| 15626 | + entityNumEntitiesActionNotPerformedWarning([Ent.USER, user], Ent.MESSAGE, jcount, Msg.NO_ENTITIES_MATCHED.format(Ent.Plural(Ent.MESSAGE)), i, count) |
| 15627 | + setSysExitRC(NO_ENTITIES_FOUND_RC) |
| 15628 | + continue |
| 15629 | + entityPerformActionModifierNumItems([Ent.USER, user], Act.MODIFIER_TO, jcount, Ent.RECIPIENT, i, count) |
| 15630 | + Ind.Increment() |
| 15631 | + j = 0 |
| 15632 | + for messageId in messageIds: |
| 15633 | + j += 1 |
| 15634 | + try: |
| 15635 | + messageInfo = callGAPI(gmail.users().messages(), 'get', |
| 15636 | + throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.INVALID_MESSAGE_ID], |
| 15637 | + userId='me', id=messageId, fields='id,threadId,payload(headers)') |
| 15638 | + threadId = messageInfo['threadId'] |
| 15639 | + msgHeaders['References'] = msgHeaders['In-Reply-To'] = _getHeaderValue('Message-ID') |
| 15640 | + msgSubject = notify['subject'] if notify['subject'] else f"Re: {_getHeaderValue('Subject')}" |
| 15641 | + recipient = _getHeaderValue('From') |
| 15642 | + send_email(msgSubject, notify['message'], recipient, j, jcount, |
| 15643 | + msgFrom=user, msgReplyTo=msgReplyTo, html=notify['html'], charset=notify['charset'], |
| 15644 | + attachments=attachments, embeddedImages=embeddedImages, msgHeaders=msgHeaders, threadId=threadId, |
| 15645 | + action=Act.SENDREPLY) |
| 15646 | + except GAPI.notFound: |
| 15647 | + entityActionFailedWarning([Ent.USER, user, Ent.MESSAGE, messageId], Msg.DOES_NOT_EXIST, j, jcount) |
| 15648 | + except GAPI.invalidMessageId: |
| 15649 | + entityActionFailedWarning([Ent.USER, user, Ent.MESSAGE, messageId], Msg.INVALID_MESSAGE_ID, j, jcount) |
| 15650 | + except (GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.invalid, GAPI.invalidArgument) as e: |
| 15651 | + entityActionFailedWarning([Ent.USER, user], str(e), i, count) |
| 15652 | + break |
| 15653 | + except GAPI.serviceNotAvailable: |
| 15654 | + userGmailServiceNotEnabledWarning(user, i, count) |
| 15655 | + break |
| 15656 | + Ind.Decrement() |
| 15657 | + |
15534 | 15658 | ADDRESS_FIELDS_PRINT_ORDER = ['contactName', 'organizationName', 'addressLine1', 'addressLine2', 'addressLine3', 'locality', 'region', 'postalCode', 'countryCode'] |
15535 | 15659 |
|
15536 | 15660 | def _showCustomerAddressPhoneNumber(customerInfo): |
@@ -80879,6 +81003,7 @@ def processResourcesCommands(): |
80879 | 81003 | 'profile': (Act.SET, setProfile), |
80880 | 81004 | 'sendas': (Act.ADD, createUpdateSendAs), |
80881 | 81005 | 'sendemail': (Act.SENDEMAIL, doSendEmail), |
| 81006 | + 'sendreply': (Act.SENDREPLY, doSendReply), |
80882 | 81007 | 'signature': (Act.SET, setSignature), |
80883 | 81008 | 'signout': (Act.SIGNOUT, signoutTurnoff2SVUsers), |
80884 | 81009 | 'turnoff2sv': (Act.TURNOFF2SV, signoutTurnoff2SVUsers), |
|
0 commit comments