mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-05 14:51:39 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa52d9e89e | ||
|
|
6383aa594a | ||
|
|
54eb59c27b | ||
|
|
3877f8309b | ||
|
|
d8b0681831 | ||
|
|
cd8303dbea | ||
|
|
ab51d6e931 | ||
|
|
b7e402dca2 | ||
|
|
842040a8b3 | ||
|
|
1205da5d34 | ||
|
|
f40824fedd | ||
|
|
67fa0cbc61 | ||
|
|
e029c77f76 | ||
|
|
2b23ae4e67 | ||
|
|
c8ecc23c9c | ||
|
|
94f8959879 | ||
|
|
2cdb8eb44d | ||
|
|
ebc1d1ecb3 | ||
|
|
d9e99334d2 | ||
|
|
a06776bbbd | ||
|
|
aecd725a71 | ||
|
|
ad8e9364e1 | ||
|
|
b812fef1c3 | ||
|
|
56371214b0 | ||
|
|
3669a86f41 | ||
|
|
4095bf63ef | ||
|
|
d75321ca8a | ||
|
|
c931e1cdd7 | ||
|
|
ea8cda72c7 | ||
|
|
f0bcd7888a | ||
|
|
6a08a66221 | ||
|
|
5e58edf598 | ||
|
|
1e79772ec1 | ||
|
|
3461ce053f | ||
|
|
9c84ce30e8 | ||
|
|
93ca63e133 | ||
|
|
e367c86fce | ||
|
|
34dc12994a | ||
|
|
df4de5ce4b | ||
|
|
ea630480b2 | ||
|
|
db1159cd0d | ||
|
|
ccf1dc0585 |
22
.travis.yml
22
.travis.yml
@@ -2,7 +2,7 @@ if: tag IS blank
|
||||
|
||||
env:
|
||||
global:
|
||||
- BUILD_PYTHON_VERSION=3.7.3
|
||||
- BUILD_PYTHON_VERSION=3.7.4
|
||||
- BUILD_OPENSSL_VERSION=1.1.1c
|
||||
- PATCHELF_VERSION=0.9
|
||||
- MUSL_VERSION=1.1.22
|
||||
@@ -134,10 +134,12 @@ install:
|
||||
script:
|
||||
- $gam version extended
|
||||
- $gam version | grep travis # travis should be part of the path (not /tmp or such)
|
||||
- if [ "$VMTYPE" == "build" ]; then $gam version | grep "Python ${BUILD_PYTHON_VERSION//./\\.}"; fi # We should be building with latest Python
|
||||
- if [ "$VMTYPE" == "build" ]; then $gam version extended | grep "OpenSSL ${BUILD_OPENSSL_VERSION//./\\.}"; fi # We should be using OpenSSL 1.1.1+
|
||||
# determine which Python version GAM is built with and ensure it's at least build version from above.
|
||||
- if [ "VMTYPE" == "build" ]; then vline=$(gamd version | grep "Python "); python_line=($vline); this_python=${python_line[1]}; tools/a_atleast_b.py $this_python $BUILD_PYTHON_VERSION; fi
|
||||
# determine which OpenSSL version GAM is built with and ensure it's at least build version from above.
|
||||
- if [ "VMTYPE" == "build" ]; then vline=$(gamd version extended | grep "OpenSSL "); openssl_line=($vline); this_openssl=${openssl_line[1]}; tools/a_atleast_b.py $this_openssl $BUILD_OPENSSL_VERSION; fi
|
||||
- if [ "$VMTYPE" == "build" ]; then $gam version extended | grep TLSv1\.[23]; fi # Builds should default TLS 1.2 or 1.3 to Google
|
||||
- if [ "$VMTYPE" == "build" ]; then GAM_TLS_MIN_VERSION=TLSv1_2 $gam version extended location tls-v1-0.badssl.com:1010; [[ $? == 3 ]]; fi # expect fail if server doesn't support our TLS version
|
||||
- if [ "$VMTYPE" == "build" ]; then GAM_TLS_MIN_VERSION=TLSv1_2 $gam version extended location tls-v1-0.badssl.com:1010; [[ $? == 3 ]]; fi # expect fail since server doesn't support our TLS version
|
||||
- export jid="$(cut -d'.' -f2 <<<"$TRAVIS_JOB_NUMBER")"
|
||||
- if [ "$TRAVIS_EVENT_TYPE" != "pull_request" ]; then export e2e=true; fi
|
||||
- if [ "$e2e" = true ]; then export gam_user=gam-travis-$jid@pdl.jaylee.us; fi
|
||||
@@ -160,12 +162,14 @@ script:
|
||||
do echo $newbase-bulkuser-$i >> sample.csv;
|
||||
done; fi
|
||||
- if [ "$e2e" = true ]; then $gam create user $newuser firstname Travis lastname $jid password random travis.jid $jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "Travis test message"; fi
|
||||
- if [ "$e2e" = true ]; then $gam create group $newgroup name "Travis $jid group" description "This is a description" isarchived true; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser add license gsuitebusiness; fi
|
||||
- if [ "$e2e" = true ]; then $gam update group $newgroup add owner $gam_user; fi
|
||||
- if [ "$e2e" = true ]; then $gam update group $newgroup add member $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam create user ~~email~~ firstname "Travis Bulk" lastname ~~email~~ travis.jid $jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user ~email add license gsuitebusiness; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "Travis test message"; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update group $newgroup add member ~email; fi
|
||||
- if [ "$e2e" = true ]; then $gam info group $newgroup; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user check serviceaccount; fi
|
||||
@@ -173,7 +177,11 @@ script:
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show imap; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $newuser delegate to ~email; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show delegates; fi
|
||||
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-01 $newbase-bulkuser-02 $newbase-bulkuser-03" delete messages query in:anywhere maxtodelete 99999 doit; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user importemail subject "Travis import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user insertemail subject "Travis insert $newbase" file gam.py labels INBOX,UNREAD; fi # yep body is gam code
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail subject "Travis send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user draftemail subject "Travis draft $newbase" message "Draft message test"; fi
|
||||
- if [ "$e2e" = true ]; then $gam users "$gam_user $newbase-bulkuser-01 $newbase-bulkuser-02 $newbase-bulkuser-03" delete messages query in:anywhere maxtodelete 99999 doit; fi
|
||||
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-04 $newbase-bulkuser-05 $newbase-bulkuser-06" trash messages query in:anywhere maxtotrash 99999 doit; fi
|
||||
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-07 $newbase-bulkuser-08 $newbase-bulkuser-09" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit; fi
|
||||
- if [ "$e2e" = true ]; then $gam create feature name Whiteboard-$newbase; fi
|
||||
@@ -197,10 +205,8 @@ script:
|
||||
- if [ "$e2e" = true ]; then $gam csv printers.csv gam info printer ~id; fi
|
||||
- if [ "$e2e" = true ]; then $gam print printjobs; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv printers.csv gam printjob ~id fetch; fi
|
||||
- if [ "$e2e" = true ]; then $gam print printjobs | $gam csv - gam printjob ~id delete; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv printers.csv gam delete printer ~id; fi
|
||||
#- if [ "$e2e" = true ]; then $gam calendar id:$newresource add editor $newuser; fi
|
||||
#- if [ "$e2e" = true ]; then $gam calendar id:$newresource update read domain; fi
|
||||
#- if [ "$e2e" = true ]; then $gam calendar id:$newresource showacl; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user ~email add calendar id:$newresource; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete resource $newresource; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete feature Whiteboard-$newbase; fi
|
||||
|
||||
@@ -143,6 +143,7 @@ If an item contains spaces, it should be surrounded by ".
|
||||
<ASPID> ::= <String>
|
||||
<BuildingID> ::= <String>|id:<String>
|
||||
<CalendarACLRole> ::= editor|freebusy|freebusyreader|owner|reader|writer
|
||||
<CalendarACLRuleID> ::= user:<EmailAddress>|group:<EmailAddress>|domain:<DomainName>|default
|
||||
<CalendarColorIndex> ::= <Number in range 1-24>
|
||||
<CalendarItem> ::= <EmailAddress>|<String>
|
||||
<ChatRoom> ::= <String>
|
||||
@@ -798,7 +799,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
<UserBasicAttribute>|
|
||||
<UserMultiAttribute>
|
||||
|
||||
gam version [check] [simple]
|
||||
gam version [check|checkrc|simple|extended] [location <HostName>]
|
||||
gam help
|
||||
|
||||
gam batch <FileName>|- [charset <Charset>]
|
||||
@@ -939,15 +940,17 @@ gam print aliases|nicknames [todrive] [shownoneditable] [nogroups] [nousers] [(q
|
||||
gam calendar <CalendarItem> add <CalendarACLRole> ([user] <EmailAddress>)|(group <EmailAddress>)|(domain [<DomainName>])|default [sendnotifications <Boolean>]
|
||||
gam calendar <CalendarItem> update <CalendarACLRole> ([user] <EmailAddress>)|(group <EmailAddress>)|(domain [<DomainName>])|default [sendnotifications <Boolean>]
|
||||
gam calendar <CalendarItem> del|delete <CalendarACLRole> <EmailAddress>|(domain [<DomainName>])|default
|
||||
gam calendar <CalendarItem> del|delete id <CalendarACLRuleID>
|
||||
gam calendar <CalendarItem> showacl
|
||||
gam calendar <CalendarItem> printacl [todrive]
|
||||
|
||||
<EventNotificationAttribute> ::=
|
||||
notifyattendees|(sendnotifications <Boolean>)|(sendupdates all|enternalonly|none)
|
||||
notifyattendees|(sendnotifications <Boolean>)|(sendupdates all|enternalonly|none)
|
||||
|
||||
The following attributes are equivalent:
|
||||
notifyattendees - sendupdates all
|
||||
sendnotifications false - sendupdates none
|
||||
sendnotifications true - sendupdates all
|
||||
notifyattendees - sendupdates all
|
||||
sendnotifications false - sendupdates none
|
||||
sendnotifications true - sendupdates all
|
||||
|
||||
<EventAttributes> ::=
|
||||
anyonecanaddself|
|
||||
@@ -973,15 +976,15 @@ The following attributes are equivalent:
|
||||
(visibility default|public|prvate)
|
||||
|
||||
<EventSelectProperty:> ::=
|
||||
(after <Time>)|
|
||||
(before <Time>)|
|
||||
includeeleted|
|
||||
includehidden|
|
||||
(query <QueryCalendar>)|
|
||||
(updatedmin <Time>)
|
||||
(after <Time>)|
|
||||
(before <Time>)|
|
||||
includeeleted|
|
||||
includehidden|
|
||||
(query <QueryCalendar>)|
|
||||
(updatedmin <Time>)
|
||||
|
||||
<EventDisplayProperty> ::=
|
||||
(timezone <TimeZone>)
|
||||
(timezone <TimeZone>)
|
||||
|
||||
gam calendar <CalendarItem> addevent <EventAttributes>+ [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarItem> deleteevent id|eventid <EventID> [doit] [<EventNotificationAttribute>]
|
||||
@@ -1089,7 +1092,7 @@ gam create resource <ResourceID> <Name> <ResourceAttributes>*
|
||||
gam update resource <ResourceID> <ResourceAttributes>*
|
||||
gam delete resource <ResourceID>
|
||||
gam info resource <ResourceID>
|
||||
gam print resources [todrive] [allfields] <ResourceFieldName>*
|
||||
gam print resources [todrive] [allfields] <ResourceFieldName>* [query <String>]
|
||||
|
||||
gam create schema|schemas <SchemaName> <SchemaFieldDefinition>+
|
||||
gam update schema <SchemaName> <SchemaFieldDefinition>* (deletefield <FieldName>)*
|
||||
@@ -1170,9 +1173,11 @@ gam print printjobs [todrive] [printer|printerid <PrinterID>]
|
||||
gam create vaultexport|export matter <MatterItem> [name <name>] corpus <drive|mail|groups|hangouts_chat>
|
||||
(accounts <EmailAddressList>) | (orgunit|ou <OrgUnitPath>) | (teamdrives <TeamDriveList>) | (rooms <ChatRoomList>) | everyone
|
||||
[scope <all_data|held_data|unprocessed_data>]
|
||||
[terms <terms>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>] [format mbox|pst]
|
||||
[excludedrafts <Boolean>] [driveversiondate <Date>|<Time>] [includeteamdrives] [includerooms]
|
||||
[includeaccessinfo <Boolean>]
|
||||
[terms <terms>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
|
||||
[excludedrafts <Boolean>] [format mbox|pst] [showconfidentialmodecontent <Boolean>]
|
||||
[includerooms]
|
||||
[driveversiondate <Date>|<Time>] [includeshareddrives|includeteamdrives] [includeaccessinfo <Boolean>]
|
||||
[region any|europe|us]
|
||||
gam delete export <MatterItem> <ExportItem>
|
||||
gam info export <MatterItem> <ExportItem>
|
||||
gam print exports [todrive] [matters <MatterItemList>]
|
||||
@@ -1283,6 +1288,21 @@ gam <UserTypeEntity> untrash messages query <QueryGmail> [doit] [max_to_untrash|
|
||||
|
||||
gam <UserTypeEntity> show gmailprofile [todrive]
|
||||
|
||||
gam <UserTypeEntity> draftemail [recipient|to <EmailAddress>] [from <EmailAddress>]
|
||||
[subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
|
||||
gam <UserTypeEntity> importemail [recipient|to <EmailAddress>] [from <EmailAddress>]
|
||||
[subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
|
||||
[labels <LabelNameList>] (header <String> <String>)*
|
||||
[deleted] [date <Time>]
|
||||
[nevercheckspam] [processforcalendar]
|
||||
gam <UserTypeEntity> insertemail [recipient|to <EmailAddress>] [from <EmailAddress>]
|
||||
[subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
|
||||
[labels <LabelNameList>] (header <String> <String>)*
|
||||
[deleted] [date <Time>]
|
||||
gam <UserTypeEntity> sendemail [recipient|to <EmailAddress>] [from <EmailAddress>]
|
||||
[subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
|
||||
(header <String> <String>)*
|
||||
|
||||
gam <UserTypeEntity> create|add delegate|delegates <EmailAddress>
|
||||
gam <UserTypeEntity> delegate|delegates to <EmailAddress>
|
||||
gam <UserTypeEntity> delete|del delegate|delegates <EmailAddress>
|
||||
|
||||
256
src/cros-aue-dates.json
Normal file
256
src/cros-aue-dates.json
Normal file
@@ -0,0 +1,256 @@
|
||||
{
|
||||
"acer ac700": "2016-08-01T00:00:00.000Z",
|
||||
"acer c7 chromebook": "2017-10-01T00:00:00.000Z",
|
||||
"acer c7 chromebook (c710)": "2017-10-01T00:00:00.000Z",
|
||||
"acer c720 chromebook": "2019-06-01T00:00:00.000Z",
|
||||
"acer c740 chromebook": "2019-06-01T00:00:00.000Z",
|
||||
"acer chromebase": "2020-08-01T00:00:00.000Z",
|
||||
"acer chromebase 24": "2021-06-01T00:00:00.000Z",
|
||||
"acer chromebook 11 (c720, c720p)": "2019-06-01T00:00:00.000Z",
|
||||
"acer chromebook 11 (c732, c732t, c732l, c732lt)": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook 11 (c740)": "2020-06-01T00:00:00.000Z",
|
||||
"acer chromebook 11 (c771, c771t)": "2022-11-01T00:00:00.000Z",
|
||||
"acer chromebook 11 (cb3-111, c730, c730e)": "2019-08-01T00:00:00.000Z",
|
||||
"acer chromebook 11 (cb3-131, c735)": "2021-01-01T00:00:00.000Z",
|
||||
"acer chromebook 11 (cb311-8h, cb311-8ht)": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook 11 n7 (c731, c731t)": "2022-01-01T00:00:00.000Z",
|
||||
"acer chromebook 13 (cb5-311)": "2019-09-01T00:00:00.000Z",
|
||||
"acer chromebook 13 (cb713-1w)": "2024-06-01T00:00:00.000Z",
|
||||
"acer chromebook 13(cb5-311, c810)": "2019-09-01T00:00:00.000Z",
|
||||
"acer chromebook 14 (cb3-431)": "2021-06-01T00:00:00.000Z",
|
||||
"acer chromebook 14 for work (cp5-471)": "2022-11-01T00:00:00.000Z",
|
||||
"acer chromebook 15 (c910 / cb5-571)": "2020-06-01T00:00:00.000Z",
|
||||
"acer chromebook 15 (cb3-531)": "2020-06-01T00:00:00.000Z",
|
||||
"acer chromebook 15 (cb3-532)": "2021-08-01T00:00:00.000Z",
|
||||
"acer chromebook 15 (cb315-1h,cb315-1ht)": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook 15 (cb5-571, c910)": "2020-06-01T00:00:00.000Z",
|
||||
"acer chromebook 15 (cb515-1h,cb515-1ht)": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook 311": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook 311 (c721, c733, c733u, c733t)": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook 315": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook 315 (cb315-2h)": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook 512 (c851, c851t)": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook 514": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook 714 (cb714-1w / cb714-1wt)": "2024-06-01T00:00:00.000Z",
|
||||
"acer chromebook 715 (cb715-1w / cb715-1wt)": "2024-06-01T00:00:00.000Z",
|
||||
"acer chromebook r11 (cb5-132t, c738t)": "2021-06-01T00:00:00.000Z",
|
||||
"acer chromebook r13 (cb5-312t)": "2021-09-01T00:00:00.000Z",
|
||||
"acer chromebook spin 11 (cp311-h1, cp311-1hn)": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook spin 11 (r751t)": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook spin 13 (cp713-1wn)": "2024-06-01T00:00:00.000Z",
|
||||
"acer chromebook spin 15 (cp315)": "2023-11-01T00:00:00.000Z",
|
||||
"acer chromebook spin 311 (r721t)": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook spin 511": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook spin 511 (r752t, r752tn)": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook spin 512 (r851tn)": "2025-06-01T00:00:00.000Z",
|
||||
"acer chromebook tab 10": "2023-08-01T00:00:00.000Z",
|
||||
"acer chromebox": "2019-09-01T00:00:00.000Z",
|
||||
"acer chromebox cxi2": "2020-06-01T00:00:00.000Z",
|
||||
"acer chromebox cxi2 / cxv2": "2020-06-01T00:00:00.000Z",
|
||||
"acer chromebox cxi3": "2024-06-01T00:00:00.000Z",
|
||||
"aopen chromebase commercial": "2020-09-01T00:00:00.000Z",
|
||||
"aopen chromebase mini": "2022-02-01T00:00:00.000Z",
|
||||
"aopen chromebox commercial": "2020-09-01T00:00:00.000Z",
|
||||
"aopen chromebox commercial 2": "2024-06-01T00:00:00.000Z",
|
||||
"aopen chromebox mini": "2022-02-01T00:00:00.000Z",
|
||||
"asi chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"asus chromebit cs10": "2020-11-01T00:00:00.000Z",
|
||||
"asus chromebook c200": "2019-06-01T00:00:00.000Z",
|
||||
"asus chromebook c200ma": "2019-06-01T00:00:00.000Z",
|
||||
"asus chromebook c201pa": "2020-06-01T00:00:00.000Z",
|
||||
"asus chromebook c202sa": "2021-06-01T00:00:00.000Z",
|
||||
"asus chromebook c204": "2025-06-01T00:00:00.000Z",
|
||||
"asus chromebook c213na": "2023-11-01T00:00:00.000Z",
|
||||
"asus chromebook c223": "2023-11-01T00:00:00.000Z",
|
||||
"asus chromebook c300": "2019-08-01T00:00:00.000Z",
|
||||
"asus chromebook c300ma": "2019-08-01T00:00:00.000Z",
|
||||
"asus chromebook c300sa / c301sa": "2021-06-01T00:00:00.000Z",
|
||||
"asus chromebook c403": "2023-11-01T00:00:00.000Z",
|
||||
"asus chromebook c423": "2023-11-01T00:00:00.000Z",
|
||||
"asus chromebook c523": "2023-11-01T00:00:00.000Z",
|
||||
"asus chromebook flip c100pa": "2020-07-01T00:00:00.000Z",
|
||||
"asus chromebook flip c101pa": "2023-08-01T00:00:00.000Z",
|
||||
"asus chromebook flip c213": "2023-11-01T00:00:00.000Z",
|
||||
"asus chromebook flip c214": "2025-06-01T00:00:00.000Z",
|
||||
"asus chromebook flip c302": "2022-11-01T00:00:00.000Z",
|
||||
"asus chromebook flip c434": "2024-06-01T00:00:00.000Z",
|
||||
"asus chromebook tablet ct100": "2023-08-01T00:00:00.000Z",
|
||||
"asus chromebox (cn60)": "2019-09-01T00:00:00.000Z",
|
||||
"asus chromebox 2 (cn62)": "2021-06-01T00:00:00.000Z",
|
||||
"asus chromebox 3": "2024-06-01T00:00:00.000Z",
|
||||
"asus chromebox 3 (cn65)": "2024-06-01T00:00:00.000Z",
|
||||
"asus chromebox cn60": "2019-09-01T00:00:00.000Z",
|
||||
"asus chromebox cn62": "2021-06-01T00:00:00.000Z",
|
||||
"bobicus chromebook 11": "2020-06-01T00:00:00.000Z",
|
||||
"chromebook 11 (c730 / cb3-111)": "2019-08-01T00:00:00.000Z",
|
||||
"chromebook 11 (c735)": "2021-01-01T00:00:00.000Z",
|
||||
"chromebook 15 (cb515 - 1ht / 1h)": "2023-11-01T00:00:00.000Z",
|
||||
"chromebook 311 (c721)": "2025-06-01T00:00:00.000Z",
|
||||
"chromebook pcm-116e": "2020-06-01T00:00:00.000Z",
|
||||
"consumer chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"cr-48": "2015-12-01T00:00:00.000Z",
|
||||
"crambo chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"ctl chromebook j41 / j41t": "2023-11-01T00:00:00.000Z",
|
||||
"ctl chromebook nl7": "2023-11-01T00:00:00.000Z",
|
||||
"ctl chromebook nl7t-360 / nl7tw-360": "2023-11-01T00:00:00.000Z",
|
||||
"ctl chromebook tab tx1": "2023-08-01T00:00:00.000Z",
|
||||
"ctl chromebook tablet tx1 for education": "2023-08-01T00:00:00.000Z",
|
||||
"ctl chromebox cbx1": "2024-06-01T00:00:00.000Z",
|
||||
"ctl j2 / j4 chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"ctl j5 chromebook": "2021-08-01T00:00:00.000Z",
|
||||
"ctl n6 education chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"ctl nl61 chromebook": "2021-08-01T00:00:00.000Z",
|
||||
"dell chromebook 11": "2019-06-01T00:00:00.000Z",
|
||||
"dell chromebook 11 (3120)": "2020-06-01T00:00:00.000Z",
|
||||
"dell chromebook 11 (3180)": "2022-05-01T00:00:00.000Z",
|
||||
"dell chromebook 11 (5190)": "2023-11-01T00:00:00.000Z",
|
||||
"dell chromebook 11 2-in-1 (3189)": "2022-05-01T00:00:00.000Z",
|
||||
"dell chromebook 11 2-in-1 (5190)": "2023-11-01T00:00:00.000Z",
|
||||
"dell chromebook 13 (3380)": "2022-11-01T00:00:00.000Z",
|
||||
"dell chromebook 13 (7310)": "2020-09-01T00:00:00.000Z",
|
||||
"dell chromebook 3100": "2025-06-01T00:00:00.000Z",
|
||||
"dell chromebook 3100 2-in-1": "2025-06-01T00:00:00.000Z",
|
||||
"dell chromebook 3400": "2025-06-01T00:00:00.000Z",
|
||||
"dell chromebox": "2019-09-01T00:00:00.000Z",
|
||||
"dell inspiron chromebook 14 2-in-1 (7486)": "2024-06-01T00:00:00.000Z",
|
||||
"edugear chromebook k": "2020-06-01T00:00:00.000Z",
|
||||
"edugear chromebook m": "2020-06-01T00:00:00.000Z",
|
||||
"edugear chromebook r": "2020-06-01T00:00:00.000Z",
|
||||
"edugear cmt chromebook": "2021-08-01T00:00:00.000Z",
|
||||
"edxis chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"edxis education chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"epik 11.6\" chromebook elb1101": "2020-06-01T00:00:00.000Z",
|
||||
"google chromebook pixel": "2018-06-01T00:00:00.000Z",
|
||||
"google chromebook pixel (2015)": "2020-06-01T00:00:00.000Z",
|
||||
"google cr-48": "2015-12-01T00:00:00.000Z",
|
||||
"google pixel slate": "2024-06-01T00:00:00.000Z",
|
||||
"google pixelbook": "2024-06-01T00:00:00.000Z",
|
||||
"haier chromebook 11": "2020-06-01T00:00:00.000Z",
|
||||
"haier chromebook 11 c": "2021-08-01T00:00:00.000Z",
|
||||
"haier chromebook 11 g2": "2020-09-01T00:00:00.000Z",
|
||||
"haier chromebook 11e": "2020-06-01T00:00:00.000Z",
|
||||
"hexa chromebook pi": "2020-06-01T00:00:00.000Z",
|
||||
"hisense chromebook 11": "2020-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11 1100-1199 / hp chromebook 11 g1": "2018-10-01T00:00:00.000Z",
|
||||
"hp chromebook 11 2000-2099 / hp chromebook 11 g2": "2019-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11 2100-2199 / hp chromebook 11 g3": "2020-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11 2200-2299 / hp chromebook 11 g4/g4 ee": "2020-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g1": "2018-10-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g2": "2019-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g3": "2020-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g4/g4 ee": "2020-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g5": "2021-07-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g5 / hp chromebook 11-vxxx": "2021-07-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g5 ee": "2022-01-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g6 ee": "2023-11-01T00:00:00.000Z",
|
||||
"hp chromebook 11 g7 ee": "2025-06-01T00:00:00.000Z",
|
||||
"hp chromebook 11a g6 ee": "2025-06-01T00:00:00.000Z",
|
||||
"hp chromebook 13 g1": "2022-11-01T00:00:00.000Z",
|
||||
"hp chromebook 14": "2019-06-01T00:00:00.000Z",
|
||||
"hp chromebook 14 / hp chromebook 14 g5": "2023-11-01T00:00:00.000Z",
|
||||
"hp chromebook 14 ak000-099 / hp chromebook 14 g4": "2021-09-01T00:00:00.000Z",
|
||||
"hp chromebook 14 db0000-db0999": "2025-06-01T00:00:00.000Z",
|
||||
"hp chromebook 14 g3": "2019-10-01T00:00:00.000Z",
|
||||
"hp chromebook 14 g4": "2021-09-01T00:00:00.000Z",
|
||||
"hp chromebook 14 g5": "2023-11-01T00:00:00.000Z",
|
||||
"hp chromebook 14 x000-x999 / hp chromebook 14 g3": "2019-10-01T00:00:00.000Z",
|
||||
"hp chromebook 14a g5": "2025-06-01T00:00:00.000Z",
|
||||
"hp chromebook 15 g1": "2024-06-01T00:00:00.000Z",
|
||||
"hp chromebook x2 ": "2024-06-01T00:00:00.000Z",
|
||||
"hp chromebook x360 11 g1 ee": "2023-11-01T00:00:00.000Z",
|
||||
"hp chromebook x360 11 g2 ee": "2025-06-01T00:00:00.000Z",
|
||||
"hp chromebook x360 14": "2024-06-01T00:00:00.000Z",
|
||||
"hp chromebook x360 14 g1": "2024-06-01T00:00:00.000Z",
|
||||
"hp chromebox cb1-(000-099) / hp chromebox g1/ hp chromebox for meetings": "2019-09-01T00:00:00.000Z",
|
||||
"hp chromebox g1": "2019-09-01T00:00:00.000Z",
|
||||
"hp chromebox g2": "2024-06-01T00:00:00.000Z",
|
||||
"hp pavilion chromebook 14": "2018-02-01T00:00:00.000Z",
|
||||
"jp sa couto chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"lava xolo chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"lenovo 100e chromebook": "2023-11-01T00:00:00.000Z",
|
||||
"lenovo 100e chromebook 2nd gen": "2025-06-01T00:00:00.000Z",
|
||||
"lenovo 100e chromebook 2nd gen mtk": "2025-06-01T00:00:00.000Z",
|
||||
"lenovo 100s chromebook": "2020-09-01T00:00:00.000Z",
|
||||
"lenovo 14e chromebook": "2025-06-01T00:00:00.000Z",
|
||||
"lenovo 300e chromebook": "2025-06-01T00:00:00.000Z",
|
||||
"lenovo 300e chromebook 2nd gen": "2025-06-01T00:00:00.000Z",
|
||||
"lenovo 300e chromebook 2nd gen mtk": "2025-06-01T00:00:00.000Z",
|
||||
"lenovo 500e chromebook": "2023-11-01T00:00:00.000Z",
|
||||
"lenovo 500e chromebook 2nd gen": "2025-06-01T00:00:00.000Z",
|
||||
"lenovo chromebook c330": "2022-06-01T00:00:00.000Z",
|
||||
"lenovo chromebook s330": "2022-06-01T00:00:00.000Z",
|
||||
"lenovo flex 11 chromebook": "2022-06-01T00:00:00.000Z",
|
||||
"lenovo ideapad c330 chromebook": "2022-06-01T00:00:00.000Z",
|
||||
"lenovo ideapad s330 chromebook": "2022-06-01T00:00:00.000Z",
|
||||
"lenovo n20 chromebook": "2019-06-01T00:00:00.000Z",
|
||||
"lenovo n21 chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"lenovo n22 chromebook": "2021-06-01T00:00:00.000Z",
|
||||
"lenovo n23 chromebook": "2021-06-01T00:00:00.000Z",
|
||||
"lenovo n23 yoga chromebook": "2022-06-01T00:00:00.000Z",
|
||||
"lenovo n42 chromebook": "2021-06-01T00:00:00.000Z",
|
||||
"lenovo thinkcentre chromebox": "2020-06-01T00:00:00.000Z",
|
||||
"lenovo thinkpad 11e 3rd gen chromebook": "2021-06-01T00:00:00.000Z",
|
||||
"lenovo thinkpad 11e 4th gen chromebook": "2023-11-01T00:00:00.000Z",
|
||||
"lenovo thinkpad 11e chromebook": "2019-06-01T00:00:00.000Z",
|
||||
"lenovo thinkpad 11e chromebook (4th gen)/lenovo thinkpad yoga 11e chromebook (4th gen)": "2023-11-01T00:00:00.000Z",
|
||||
"lenovo thinkpad 13": "2022-11-01T00:00:00.000Z",
|
||||
"lenovo thinkpad x131e chromebook": "2018-06-01T00:00:00.000Z",
|
||||
"lenovo yoga c630 chromebook": "2024-06-01T00:00:00.000Z",
|
||||
"lg chromebase (22cb25s)": "2020-06-01T00:00:00.000Z",
|
||||
"lg chromebase (22cv241)": "2019-06-01T00:00:00.000Z",
|
||||
"lumos education chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"m&a chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"mecer chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"mecer v2 chromebook": "2021-08-01T00:00:00.000Z",
|
||||
"medion chromebook akoya s2013 ": "2020-06-01T00:00:00.000Z",
|
||||
"medion chromebook s2015": "2020-06-01T00:00:00.000Z",
|
||||
"multilaser chromebook m11c": "2021-08-01T00:00:00.000Z",
|
||||
"ncomputing chromebook cx100": "2020-06-01T00:00:00.000Z",
|
||||
"ncomputing chromebook cx110": "2020-06-01T00:00:00.000Z",
|
||||
"nexian chromebook 11.6\"": "2020-06-01T00:00:00.000Z",
|
||||
"pcmerge chromebook al116": "2023-11-01T00:00:00.000Z",
|
||||
"pcmerge chromebookpcm-116e/pcm-116eb": "2020-06-01T00:00:00.000Z",
|
||||
"pcmerge chromebookpcm-116t-432b": "2021-08-01T00:00:00.000Z",
|
||||
"poin2 chromebook 11": "2020-06-01T00:00:00.000Z",
|
||||
"poin2 chromebook 11c": "2022-11-01T00:00:00.000Z",
|
||||
"poin2 chromebook 14": "2022-03-01T00:00:00.000Z",
|
||||
"positivo chromebook c216b": "2021-08-01T00:00:00.000Z",
|
||||
"positivo chromebook ch1190": "2020-06-01T00:00:00.000Z",
|
||||
"promethean chromebox": "2024-06-01T00:00:00.000Z",
|
||||
"prowise 11.6\" entry line chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"prowise chromebook eduline": "2023-11-01T00:00:00.000Z",
|
||||
"prowise chromebook entryline": "2020-06-01T00:00:00.000Z",
|
||||
"prowise chromebook proline": "2021-08-01T00:00:00.000Z",
|
||||
"prowise proline chromebook": "2021-08-01T00:00:00.000Z",
|
||||
"rgs education chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"samsung chromebook": "2018-07-01T00:00:00.000Z",
|
||||
"samsung chromebook - xe303": "2018-07-01T00:00:00.000Z",
|
||||
"samsung chromebook 2 11": "2019-06-01T00:00:00.000Z",
|
||||
"samsung chromebook 2 11 - xe500c12": "2020-06-01T00:00:00.000Z",
|
||||
"samsung chromebook 2 13": "2019-06-01T00:00:00.000Z",
|
||||
"samsung chromebook 3": "2021-06-01T00:00:00.000Z",
|
||||
"samsung chromebook plus": "2023-08-01T00:00:00.000Z",
|
||||
"samsung chromebook plus (lte)": "2024-06-01T00:00:00.000Z",
|
||||
"samsung chromebook plus (v2)": "2024-06-01T00:00:00.000Z",
|
||||
"samsung chromebook pro": "2022-11-01T00:00:00.000Z",
|
||||
"samsung chromebook series 5": "2016-06-01T00:00:00.000Z",
|
||||
"samsung chromebook series 5 550": "2017-05-01T00:00:00.000Z",
|
||||
"samsung chromebox series 3": "2018-03-01T00:00:00.000Z",
|
||||
"sector 5 e1 rugged chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"sector 5 e3 chromebook": "2023-11-01T00:00:00.000Z",
|
||||
"senkatel c1101 chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"thinkpad 11e chromebook 3rd gen (yoga/clamshell)": "2021-06-01T00:00:00.000Z",
|
||||
"thinkpad 13 chromebook": "2022-11-01T00:00:00.000Z",
|
||||
"toshiba chromebook": "2019-06-01T00:00:00.000Z",
|
||||
"toshiba chromebook 2": "2020-06-01T00:00:00.000Z",
|
||||
"toshiba chromebook 2 (2015 edition)": "2020-09-01T00:00:00.000Z",
|
||||
"true idc chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"true idc chromebook 11": "2020-06-01T00:00:00.000Z",
|
||||
"videonet chromebook": "2020-06-01T00:00:00.000Z",
|
||||
"videonet chromebook bl10": "2020-06-01T00:00:00.000Z",
|
||||
"viewsonic nmp660 chromebox": "2024-06-01T00:00:00.000Z",
|
||||
"viglen chromebook 11": "2020-06-01T00:00:00.000Z",
|
||||
"viglen chromebook 11c": "2023-11-01T00:00:00.000Z",
|
||||
"viglen chromebook 360": "2021-08-01T00:00:00.000Z",
|
||||
"xolo chromebook": "2020-06-01T00:00:00.000Z"
|
||||
}
|
||||
484
src/gam.py
484
src/gam.py
@@ -27,6 +27,7 @@ import configparser
|
||||
import csv
|
||||
import datetime
|
||||
import difflib
|
||||
from email import message_from_string
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
@@ -50,12 +51,10 @@ import uuid
|
||||
import webbrowser
|
||||
import zipfile
|
||||
import http.client as http_client
|
||||
from email.mime.text import MIMEText
|
||||
from multiprocessing import Pool
|
||||
from multiprocessing import freeze_support
|
||||
from urllib.parse import urlencode, urlparse
|
||||
from passlib.hash import sha512_crypt
|
||||
import dns.resolver
|
||||
import dateutil.parser
|
||||
|
||||
import googleapiclient
|
||||
@@ -113,32 +112,6 @@ def _createHttpObj(cache=None, override_min_tls=None, override_max_tls=None):
|
||||
return httplib2.Http(tls_maximum_version=tls_maximum_version, tls_minimum_version=tls_minimum_version,
|
||||
cache=cache)
|
||||
|
||||
# Override google_auth_oauthlib classes so we use PKCE
|
||||
# Remove once https://github.com/googleapis/google-auth-library-python-oauthlib/pull/42
|
||||
# is landed and released.
|
||||
def _authorization_url(self, **kwargs):
|
||||
kwargs.setdefault('access_type', 'offline')
|
||||
rnd = SystemRandom()
|
||||
random_verifier = [rnd.choice(URL_SAFE_CHARS) for _ in range(128)]
|
||||
self.code_verifier = ''.join(random_verifier)
|
||||
code_hash = hashlib.sha256()
|
||||
code_hash.update(str.encode(self.code_verifier))
|
||||
unencoded_challenge = code_hash.digest()
|
||||
b64_challenge = base64.urlsafe_b64encode(unencoded_challenge)
|
||||
code_challenge = b64_challenge.decode().split('=')[0]
|
||||
kwargs.setdefault('code_challenge', code_challenge)
|
||||
kwargs.setdefault('code_challenge_method', 'S256')
|
||||
url, state = self.oauth2session.authorization_url(self.client_config['auth_uri'], **kwargs)
|
||||
return url, state
|
||||
|
||||
def _fetch_token(self, **kwargs):
|
||||
kwargs.setdefault('client_secret', self.client_config['client_secret'])
|
||||
kwargs.setdefault('code_verifier', self.code_verifier)
|
||||
return self.oauth2session.fetch_token(self.client_config['token_uri'], **kwargs)
|
||||
|
||||
google_auth_oauthlib.flow.Flow.authorization_url = _authorization_url
|
||||
google_auth_oauthlib.flow.Flow.fetch_token = _fetch_token
|
||||
|
||||
def showUsage():
|
||||
doGAMVersion(checkForArgs=False)
|
||||
print('''
|
||||
@@ -630,7 +603,7 @@ def SetGlobalVariables():
|
||||
|
||||
ROW_FILTER_COMP_PATTERN = re.compile(r'^(date|time|count)\s*([<>]=?|=|!=)\s*(.+)$', re.IGNORECASE)
|
||||
ROW_FILTER_BOOL_PATTERN = re.compile(r'^(boolean):(.+)$', re.IGNORECASE)
|
||||
ROW_FILTER_RE_PATTERN = re.compile(r'^(regex):(.+)$', re.IGNORECASE)
|
||||
ROW_FILTER_RE_PATTERN = re.compile(r'^(regex|notregex):(.+)$', re.IGNORECASE)
|
||||
|
||||
def _getCfgRowFilter(itemName):
|
||||
value = GC_Defaults[itemName]
|
||||
@@ -673,7 +646,7 @@ def SetGlobalVariables():
|
||||
continue
|
||||
except re.error as e:
|
||||
systemErrorExit(3, 'Item: {0}, Value: "{1}": "{2}", Invalid RE: {3}'.format(itemName, column, filterStr, e))
|
||||
systemErrorExit(3, 'Item: {0}, Value: "{1}": {2}, Expected: (date|time|count<Operator><Value>) or (boolean:true|false) or (regex:<RegularExpression>)'.format(itemName, column, filterStr))
|
||||
systemErrorExit(3, 'Item: {0}, Value: "{1}": {2}, Expected: (date|time|count<Operator><Value>) or (boolean:true|false) or (regex|notregex:<RegularExpression>)'.format(itemName, column, filterStr))
|
||||
return rowFilters
|
||||
except (TypeError, ValueError) as e:
|
||||
systemErrorExit(3, 'Item: {0}, Value: "{1}", Failed to parse as JSON: {2}'.format(itemName, value, str(e)))
|
||||
@@ -848,10 +821,17 @@ def _getServerTLSUsed(location):
|
||||
conn = 'https:%s' % netloc
|
||||
httpc = _createHttpObj()
|
||||
headers = {'user-agent': GAM_INFO}
|
||||
try:
|
||||
httpc.request(url, headers=headers)
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
systemErrorExit(4, e)
|
||||
retries = 5
|
||||
for n in range(1, retries+1):
|
||||
try:
|
||||
httpc.request(url, headers=headers)
|
||||
break
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
if n != retries:
|
||||
httpc.connections = {}
|
||||
waitOnFailure(n, retries, str(e))
|
||||
continue
|
||||
systemErrorExit(4, str(e))
|
||||
cipher_name, tls_ver, _ = httpc.connections[conn].sock.cipher()
|
||||
return tls_ver, cipher_name
|
||||
|
||||
@@ -1092,7 +1072,13 @@ def callGAPI(service, function,
|
||||
service._http.cache = None
|
||||
continue
|
||||
systemErrorExit(4, str(e))
|
||||
except (TypeError, httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
if n != retries:
|
||||
service._http.connections = {}
|
||||
waitOnFailure(n, retries, str(e))
|
||||
continue
|
||||
systemErrorExit(4, str(e))
|
||||
except TypeError as e:
|
||||
systemErrorExit(4, str(e))
|
||||
|
||||
def callGAPIpages(service, function, items='items',
|
||||
@@ -1252,11 +1238,19 @@ def getValidOauth2TxtCredentials(force_refresh=False):
|
||||
"""Gets OAuth2 credentials which are guaranteed to be fresh and valid."""
|
||||
credentials = getOauth2TxtStorageCredentials()
|
||||
if (credentials and credentials.expired) or force_refresh:
|
||||
try:
|
||||
credentials.refresh(google_auth_httplib2.Request(_createHttpObj()))
|
||||
writeCredentials(credentials)
|
||||
except google.auth.exceptions.RefreshError as e:
|
||||
systemErrorExit(18, str(e))
|
||||
retries = 3
|
||||
for n in range(1, retries+1):
|
||||
try:
|
||||
credentials.refresh(google_auth_httplib2.Request(_createHttpObj()))
|
||||
writeCredentials(credentials)
|
||||
break
|
||||
except google.auth.exceptions.RefreshError as e:
|
||||
systemErrorExit(18, str(e))
|
||||
except (google.auth.exceptions.TransportError, httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
if n != retries:
|
||||
waitOnFailure(n, retries, str(e))
|
||||
continue
|
||||
systemErrorExit(4, str(e))
|
||||
elif credentials is None or not credentials.valid:
|
||||
doRequestOAuth()
|
||||
credentials = getOauth2TxtStorageCredentials()
|
||||
@@ -1283,6 +1277,10 @@ def getService(api, http):
|
||||
http.cache = None
|
||||
return service
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
if n != retries:
|
||||
http.connections = {}
|
||||
waitOnFailure(n, retries, str(e))
|
||||
continue
|
||||
systemErrorExit(4, str(e))
|
||||
except (googleapiclient.errors.InvalidJsonError, KeyError, ValueError) as e:
|
||||
http.cache = None
|
||||
@@ -1410,17 +1408,24 @@ def buildGAPIServiceObject(api, act_as, showAuthError=True):
|
||||
GM_Globals[GM_CURRENT_API_SCOPES] = API_SCOPE_MAPPING[api]
|
||||
credentials = getSvcAcctCredentials(GM_Globals[GM_CURRENT_API_SCOPES], act_as)
|
||||
request = google_auth_httplib2.Request(http)
|
||||
try:
|
||||
credentials.refresh(request)
|
||||
service._http = google_auth_httplib2.AuthorizedHttp(credentials, http=http)
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
systemErrorExit(4, e)
|
||||
except google.auth.exceptions.RefreshError as e:
|
||||
if isinstance(e.args, tuple):
|
||||
e = e.args[0]
|
||||
if showAuthError:
|
||||
stderrErrorMsg('User {0}: {1}'.format(GM_Globals[GM_CURRENT_API_USER], str(e)))
|
||||
return handleOAuthTokenError(str(e), True)
|
||||
retries = 3
|
||||
for n in range(1, retries+1):
|
||||
try:
|
||||
credentials.refresh(request)
|
||||
service._http = google_auth_httplib2.AuthorizedHttp(credentials, http=http)
|
||||
break
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
if n != retries:
|
||||
http.connections = {}
|
||||
waitOnFailure(n, retries, str(e))
|
||||
continue
|
||||
systemErrorExit(4, e)
|
||||
except google.auth.exceptions.RefreshError as e:
|
||||
if isinstance(e.args, tuple):
|
||||
e = e.args[0]
|
||||
if showAuthError:
|
||||
stderrErrorMsg('User {0}: {1}'.format(GM_Globals[GM_CURRENT_API_USER], str(e)))
|
||||
return handleOAuthTokenError(str(e), True)
|
||||
return service
|
||||
|
||||
def buildAlertCenterGAPIObject(user):
|
||||
@@ -1969,9 +1974,6 @@ def doDelCourse():
|
||||
callGAPI(croom.courses(), 'delete', id=courseId)
|
||||
print('Deleted Course %s' % courseId)
|
||||
|
||||
def _getValidCourseStates(croom):
|
||||
return [state for state in croom._rootDesc['schemas']['Course']['properties']['courseState']['enum'] if state != 'COURSE_STATE_UNSPECIFIED']
|
||||
|
||||
def _getValidatedState(state, validStates):
|
||||
state = state.upper()
|
||||
if state not in validStates:
|
||||
@@ -1992,13 +1994,13 @@ def getCourseAttribute(myarg, value, body, croom, function):
|
||||
elif myarg in ['owner', 'ownerid', 'teacher']:
|
||||
body['ownerId'] = normalizeEmailAddressOrUID(value)
|
||||
elif myarg in ['state', 'status']:
|
||||
validStates = _getValidCourseStates(croom)
|
||||
validStates = _getEnumValuesMinusUnspecified(croom._rootDesc['schemas']['Course']['properties']['courseState']['enum'])
|
||||
body['courseState'] = _getValidatedState(value, validStates)
|
||||
else:
|
||||
systemErrorExit(2, '%s is not a valid argument to "gam %s course"' % (myarg, function))
|
||||
|
||||
def _getCourseStates(croom, value, courseStates):
|
||||
validStates = _getValidCourseStates(croom)
|
||||
validStates = _getEnumValuesMinusUnspecified(croom._rootDesc['schemas']['Course']['properties']['courseState']['enum'])
|
||||
for state in value.replace(',', ' ').split():
|
||||
courseStates.append(_getValidatedState(state, validStates))
|
||||
|
||||
@@ -2053,11 +2055,11 @@ def doGetDomainInfo():
|
||||
domainName = sys.argv[3]
|
||||
result = callGAPI(cd.domains(), 'get', customer=GC_Values[GC_CUSTOMER_ID], domainName=domainName)
|
||||
if 'creationTime' in result:
|
||||
result['creationTime'] = str(datetime.datetime.fromtimestamp(int(result['creationTime'])/1000))
|
||||
result['creationTime'] = utils.formatTimestampYMDHMSF(result['creationTime'])
|
||||
if 'domainAliases' in result:
|
||||
for i in range(0, len(result['domainAliases'])):
|
||||
if 'creationTime' in result['domainAliases'][i]:
|
||||
result['domainAliases'][i]['creationTime'] = str(datetime.datetime.fromtimestamp(int(result['domainAliases'][i]['creationTime'])/1000))
|
||||
result['domainAliases'][i]['creationTime'] = utils.formatTimestampYMDHMSF(result['domainAliases'][i]['creationTime'])
|
||||
print_json(None, result)
|
||||
|
||||
def doGetDomainAliasInfo():
|
||||
@@ -2065,7 +2067,7 @@ def doGetDomainAliasInfo():
|
||||
alias = sys.argv[3]
|
||||
result = callGAPI(cd.domainAliases(), 'get', customer=GC_Values[GC_CUSTOMER_ID], domainAliasName=alias)
|
||||
if 'creationTime' in result:
|
||||
result['creationTime'] = str(datetime.datetime.fromtimestamp(int(result['creationTime'])/1000))
|
||||
result['creationTime'] = utils.formatTimestampYMDHMSF(result['creationTime'])
|
||||
print_json(None, result)
|
||||
|
||||
def doGetCustomerInfo():
|
||||
@@ -2185,7 +2187,7 @@ def doPrintDomains(return_results=False):
|
||||
if attr in ['kind', 'etag', 'domainAliases', 'isPrimary']:
|
||||
continue
|
||||
if attr in ['creationTime',]:
|
||||
domain[attr] = str(datetime.datetime.fromtimestamp(int(domain[attr])/1000))
|
||||
domain[attr] = utils.formatTimestampYMDHMSF(domain[attr])
|
||||
if attr not in titles:
|
||||
titles.append(attr)
|
||||
domain_attributes[attr] = domain[attr]
|
||||
@@ -2200,7 +2202,7 @@ def doPrintDomains(return_results=False):
|
||||
if attr in ['kind', 'etag']:
|
||||
continue
|
||||
if attr in ['creationTime',]:
|
||||
aliasdomain[attr] = str(datetime.datetime.fromtimestamp(int(aliasdomain[attr])/1000))
|
||||
aliasdomain[attr] = utils.formatTimestampYMDHMSF(aliasdomain[attr])
|
||||
if attr not in titles:
|
||||
titles.append(attr)
|
||||
aliasdomain_attributes[attr] = aliasdomain[attr]
|
||||
@@ -2229,7 +2231,7 @@ def doPrintDomainAliases():
|
||||
if attr in ['kind', 'etag']:
|
||||
continue
|
||||
if attr == 'creationTime':
|
||||
domainAlias[attr] = str(datetime.datetime.fromtimestamp(int(domainAlias[attr])/1000))
|
||||
domainAlias[attr] = utils.formatTimestampYMDHMSF(domainAlias[attr])
|
||||
if attr not in titles:
|
||||
titles.append(attr)
|
||||
domainAlias_attributes[attr] = domainAlias[attr]
|
||||
@@ -3074,9 +3076,8 @@ def doPrintPrintJobs():
|
||||
jobCount = totalJobs
|
||||
break
|
||||
continue
|
||||
updateTime = int(job['updateTime'])/1000
|
||||
job['createTime'] = datetime.datetime.fromtimestamp(createTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
job['updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
job['createTime'] = utils.formatTimestampYMDHMS(job['createTime'])
|
||||
job['updateTime'] = utils.formatTimestampYMDHMS(job['updateTime'])
|
||||
job['tags'] = ' '.join(job['tags'])
|
||||
addRowTitlesToCSVfile(flatten_json(job), csvRows, titles)
|
||||
if jobCount >= totalJobs:
|
||||
@@ -3116,12 +3117,9 @@ def doPrintPrinters():
|
||||
printers = callGAPI(cp.printers(), 'list', q=query, type=printer_type, connection_status=connection_status, extra_fields=extra_fields)
|
||||
checkCloudPrintResult(printers)
|
||||
for printer in printers['printers']:
|
||||
createTime = int(printer['createTime'])/1000
|
||||
accessTime = int(printer['accessTime'])/1000
|
||||
updateTime = int(printer['updateTime'])/1000
|
||||
printer['createTime'] = datetime.datetime.fromtimestamp(createTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
printer['accessTime'] = datetime.datetime.fromtimestamp(accessTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
printer['updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
printer['createTime'] = utils.formatTimestampYMDHMS(printer['createTime'])
|
||||
printer['accessTime'] = utils.formatTimestampYMDHMS(printer['accessTime'])
|
||||
printer['updateTime'] = utils.formatTimestampYMDHMS(printer['updateTime'])
|
||||
printer['tags'] = ' '.join(printer['tags'])
|
||||
addRowTitlesToCSVfile(flatten_json(printer), csvRows, titles)
|
||||
writeCSVfile(csvRows, titles, 'Printers', todrive)
|
||||
@@ -3544,12 +3542,9 @@ def doGetPrinterInfo():
|
||||
result = callGAPI(cp.printers(), 'get', printerid=printerid)
|
||||
checkCloudPrintResult(result)
|
||||
printer_info = result['printers'][0]
|
||||
createTime = int(printer_info['createTime'])/1000
|
||||
accessTime = int(printer_info['accessTime'])/1000
|
||||
updateTime = int(printer_info['updateTime'])/1000
|
||||
printer_info['createTime'] = datetime.datetime.fromtimestamp(createTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
printer_info['accessTime'] = datetime.datetime.fromtimestamp(accessTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
printer_info['updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
printer_info['createTime'] = utils.formatTimestampYMDHMS(printer_info['createTime'])
|
||||
printer_info['accessTime'] = utils.formatTimestampYMDHMS(printer_info['accessTime'])
|
||||
printer_info['updateTime'] = utils.formatTimestampYMDHMS(printer_info['updateTime'])
|
||||
printer_info['tags'] = ' '.join(printer_info['tags'])
|
||||
if not everything:
|
||||
del printer_info['capabilities']
|
||||
@@ -3705,20 +3700,29 @@ def formatACLRule(rule):
|
||||
return '(Scope: {0}:{1}, Role: {2})'.format(rule['scope']['type'], rule['scope']['value'], rule['role'])
|
||||
return '(Scope: {0}, Role: {1})'.format(rule['scope']['type'], rule['role'])
|
||||
|
||||
def doCalendarShowPrintACL(csvOut=False):
|
||||
def doCalendarPrintShowACLs(csvFormat):
|
||||
calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2])
|
||||
if not cal:
|
||||
return
|
||||
toDrive = False
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if csvFormat and myarg == 'todrive':
|
||||
toDrive = True
|
||||
i += 1
|
||||
else:
|
||||
systemErrorExit(2, '%s is not a valid argument for "gam calendar <email> %s"' % (sys.argv[i], ['showacl', 'printacl'][csvFormat]))
|
||||
acls = callGAPIpages(cal.acl(), 'list', 'items', calendarId=calendarId)
|
||||
i = 0
|
||||
if csvOut:
|
||||
if csvFormat:
|
||||
titles = []
|
||||
rows = []
|
||||
else:
|
||||
count = len(acls)
|
||||
for rule in acls:
|
||||
i += 1
|
||||
if csvOut:
|
||||
if csvFormat:
|
||||
row = flatten_json(rule, None)
|
||||
for key in row:
|
||||
if key not in titles:
|
||||
@@ -3726,8 +3730,8 @@ def doCalendarShowPrintACL(csvOut=False):
|
||||
rows.append(row)
|
||||
else:
|
||||
print('Calendar: {0}, ACL: {1}{2}'.format(calendarId, formatACLRule(rule), currentCount(i, count)))
|
||||
if csvOut:
|
||||
writeCSVfile(rows, titles, '%s Calendar ACLs' % calendarId, False)
|
||||
if csvFormat:
|
||||
writeCSVfile(rows, titles, '%s Calendar ACLs' % calendarId, toDrive)
|
||||
|
||||
def _getCalendarACLScope(i, body):
|
||||
body['scope'] = {}
|
||||
@@ -3783,15 +3787,15 @@ def doCalendarDelACL():
|
||||
calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2])
|
||||
if not cal:
|
||||
return
|
||||
if sys.argv[4].lower() == 'user':
|
||||
if sys.argv[4].lower() == 'id':
|
||||
ruleId = sys.argv[5]
|
||||
print('Removing rights for %s to %s' % (ruleId, calendarId))
|
||||
callGAPI(cal.acl(), 'delete', calendarId=calendarId, ruleId=ruleId)
|
||||
else:
|
||||
body = {'role': 'none'}
|
||||
_getCalendarACLScope(5, body)
|
||||
print('Calendar: {0}, {1} ACL: {2}'.format(calendarId, 'Delete', formatACLScope(body)))
|
||||
callGAPI(cal.acl(), 'insert', calendarId=calendarId, body=body, sendNotifications=False)
|
||||
elif sys.argv[4].lower() == 'id':
|
||||
ruleId = sys.argv[5]
|
||||
print('Removing rights for %s to %s' % (ruleId, calendarId))
|
||||
callGAPI(cal.acl(), 'delete', calendarId=calendarId, ruleId=ruleId)
|
||||
|
||||
def doCalendarWipeData():
|
||||
calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2])
|
||||
@@ -4687,11 +4691,25 @@ def deleteDriveFile(users):
|
||||
file_ids = [fileIds,]
|
||||
if not file_ids:
|
||||
print('No files to %s for %s' % (function, user))
|
||||
i = 0
|
||||
j = 0
|
||||
batch_size = 10
|
||||
del_me_count = len(file_ids)
|
||||
dbatch = drive.new_batch_http_request(callback=drive_del_result)
|
||||
method = getattr(drive.files(), function)
|
||||
for fileId in file_ids:
|
||||
i += 1
|
||||
print('%s %s for %s (%s/%s)' % (action, fileId, user, i, len(file_ids)))
|
||||
callGAPI(drive.files(), function, fileId=fileId, supportsAllDrives=True)
|
||||
j += 1
|
||||
dbatch.add(method(fileId=fileId, supportsAllDrives=True))
|
||||
if len(dbatch._order) == batch_size:
|
||||
print('%s %s files...' % (action, len(dbatch._order)))
|
||||
dbatch.execute()
|
||||
dbatch = drive.new_batch_http_request(callback=drive_del_result)
|
||||
if len(dbatch._order) > 0:
|
||||
print('%s %s files...' % (action, len(dbatch._order)))
|
||||
dbatch.execute()
|
||||
|
||||
def drive_del_result(request_id, response, exception):
|
||||
if exception:
|
||||
print(exception)
|
||||
|
||||
def printDriveFolderContents(feed, folderId, indent):
|
||||
for f_file in feed:
|
||||
@@ -5336,6 +5354,58 @@ def transferDriveFiles(users):
|
||||
if not skipped_files:
|
||||
break
|
||||
|
||||
def sendOrDropEmail(users, method='send'):
|
||||
body = subject = ''
|
||||
recipient = labels = sender = None
|
||||
kwargs = {}
|
||||
if method in ['insert', 'import']:
|
||||
kwargs['internalDateSource'] = 'receivedTime'
|
||||
if method == 'import':
|
||||
kwargs['neverMarkSpam'] = True
|
||||
msgHeaders = {}
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'message':
|
||||
body = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'file':
|
||||
filename = sys.argv[i+1]
|
||||
i, encoding = getCharSet(i+2)
|
||||
body = readFile(filename, encoding=encoding)
|
||||
elif myarg == 'subject':
|
||||
subject = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg in ['recipient', 'to']:
|
||||
recipient = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'from':
|
||||
sender = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'header':
|
||||
msgHeaders[sys.argv[i+1]] = sys.argv[i+2]
|
||||
i += 3
|
||||
elif method in ['insert', 'import'] and myarg == 'labels':
|
||||
labels = shlexSplitList(sys.argv[i+1])
|
||||
i += 2
|
||||
elif method in ['insert', 'import'] and myarg == 'deleted':
|
||||
kwargs['deleted'] = True
|
||||
i += 1
|
||||
elif myarg == 'date':
|
||||
msgHeaders['Date'] = getTimeOrDeltaFromNow(sys.argv[i+1])
|
||||
if method in ['insert', 'import']:
|
||||
kwargs['internalDateSource'] = 'dateHeader'
|
||||
i += 2
|
||||
elif method == 'import' and myarg == 'checkspam':
|
||||
kwargs['neverMarkSpam'] = False
|
||||
i += 1
|
||||
elif method == 'import' and myarg == 'processforcalendar':
|
||||
kwargs['processForCalendar'] = True
|
||||
else:
|
||||
systemErrorExit(2, '%s is not a valid argument for "gam <users> %semail"' % (sys.argv[i], method))
|
||||
for user in users:
|
||||
send_email(subject, body, recipient, sender, user, method, labels, msgHeaders, kwargs)
|
||||
|
||||
def doImap(users):
|
||||
enable = getBoolean(sys.argv[4], 'gam <users> imap')
|
||||
body = {'enabled': enable, 'autoExpunge': True, 'expungeBehavior': 'archive', 'maxFolderSize': 0}
|
||||
@@ -5782,7 +5852,7 @@ def printShowSmime(users, csvFormat):
|
||||
result = callGAPI(gmail.users().settings().sendAs().smimeInfo(), 'list', sendAsEmail=sendAsEmail, userId='me')
|
||||
smimes = result.get('smimeInfo', [])
|
||||
for j, _ in enumerate(smimes):
|
||||
smimes[j]['expiration'] = datetime.datetime.fromtimestamp(int(smimes[j]['expiration'])/1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
smimes[j]['expiration'] = utils.formatTimestampYMDHMS(smimes[j]['expiration'])
|
||||
if csvFormat:
|
||||
for smime in smimes:
|
||||
addRowTitlesToCSVfile(flatten_json(smime, flattened={'User': user}), csvRows, titles)
|
||||
@@ -6886,11 +6956,11 @@ def getVacation(users):
|
||||
print(' Contacts Only: {0}'.format(result['restrictToContacts']))
|
||||
print(' Domain Only: {0}'.format(result['restrictToDomain']))
|
||||
if 'startTime' in result:
|
||||
print(' Start Date: {0}'.format(datetime.datetime.fromtimestamp(int(result['startTime'])/1000).strftime('%Y-%m-%d')))
|
||||
print(' Start Date: {0}'.format(utils.formatTimestampYMD(result['startTime'])))
|
||||
else:
|
||||
print(' Start Date: Started')
|
||||
if 'endTime' in result:
|
||||
print(' End Date: {0}'.format(datetime.datetime.fromtimestamp(int(result['endTime'])/1000).strftime('%Y-%m-%d')))
|
||||
print(' End Date: {0}'.format(utils.formatTimestampYMD(result['endTime'])))
|
||||
else:
|
||||
print(' End Date: Not specified')
|
||||
print(utils.convertUTF8(' Subject: {0}'.format(result.get('responseSubject', 'None'))))
|
||||
@@ -7573,10 +7643,11 @@ def getCRMService(login_hint):
|
||||
discoveryServiceUrl=googleapiclient.discovery.V2_DISCOVERY_URI),
|
||||
http)
|
||||
|
||||
def getGAMProjectAPIs():
|
||||
def getGAMProjectFile(filepath):
|
||||
file_url = GAM_PROJECT_FILEPATH+filepath
|
||||
httpObj = _createHttpObj()
|
||||
_, c = httpObj.request(GAM_PROJECT_APIS, 'GET')
|
||||
return httpObj, c.decode(UTF8).splitlines()
|
||||
_, c = httpObj.request(file_url, 'GET')
|
||||
return c.decode(UTF8)
|
||||
|
||||
def enableGAMProjectAPIs(GAMProjectAPIs, httpObj, projectId, checkEnabled, i=0, count=0):
|
||||
apis = GAMProjectAPIs[:]
|
||||
@@ -7656,7 +7727,7 @@ def _createClientSecretsOauth2service(httpObj, projectId):
|
||||
print('Unknown error: %s' % content)
|
||||
return False
|
||||
|
||||
simplehttp, GAMProjectAPIs = getGAMProjectAPIs()
|
||||
GAMProjectAPIs = getGAMProjectFile('src/project-apis.txt').splitlines()
|
||||
enableGAMProjectAPIs(GAMProjectAPIs, httpObj, projectId, False)
|
||||
iam = googleapiclient.discovery.build('iam', 'v1',
|
||||
http=httpObj, cache_discovery=False,
|
||||
@@ -7701,6 +7772,7 @@ def _createClientSecretsOauth2service(httpObj, projectId):
|
||||
client_secret = input('Enter your Client Secret: ').strip()
|
||||
if not client_secret:
|
||||
client_secret = input().strip()
|
||||
simplehttp = _createHttpObj()
|
||||
client_valid = _checkClientAndSecret(simplehttp, client_id, client_secret)
|
||||
if client_valid:
|
||||
break
|
||||
@@ -7907,7 +7979,7 @@ def doUseProject():
|
||||
|
||||
def doUpdateProjects():
|
||||
_, httpObj, login_hint, projects, _ = _getLoginHintProjects(False)
|
||||
_, GAMProjectAPIs = getGAMProjectAPIs()
|
||||
GAMProjectAPIs = getGAMProjectFile('src/project-apis.txt').splitlines()
|
||||
count = len(projects)
|
||||
print('User: {0}, Update {1} Projects'.format(login_hint, count))
|
||||
i = 0
|
||||
@@ -8159,25 +8231,20 @@ VAULT_SEARCH_METHODS_MAP = {
|
||||
'ou': 'ORG_UNIT',
|
||||
'room': 'ROOM',
|
||||
'rooms': 'ROOM',
|
||||
'teamdrive': 'TEAM_DRIVE',
|
||||
'teamdrives': 'TEAM_DRIVE',
|
||||
'shareddrive': 'SHARED_DRIVE',
|
||||
'shareddrives': 'SHARED_DRIVE',
|
||||
'teamdrive': 'SHARED_DRIVE',
|
||||
'teamdrives': 'SHARED_DRIVE',
|
||||
}
|
||||
VAULT_SEARCH_METHODS_LIST = ['accounts', 'orgunit', 'teamdrives', 'rooms', 'everyone']
|
||||
VAULT_SEARCH_METHODS_LIST = ['accounts', 'orgunit', 'shareddrives', 'rooms', 'everyone']
|
||||
|
||||
def doCreateVaultExport():
|
||||
v = buildGAPIObject('vault')
|
||||
allowed_corpuses = v._rootDesc['schemas']['Query']['properties']['corpus']['enum']
|
||||
try:
|
||||
allowed_corpuses.remove('CORPUS_TYPE_UNSPECIFIED')
|
||||
except ValueError:
|
||||
pass
|
||||
allowed_scopes = v._rootDesc['schemas']['Query']['properties']['dataScope']['enum']
|
||||
try:
|
||||
allowed_scopes.remove('DATA_SCOPE_UNSPECIFIED')
|
||||
except ValueError:
|
||||
pass
|
||||
allowed_formats = ['MBOX', 'PST']
|
||||
allowed_corpuses = _getEnumValuesMinusUnspecified(v._rootDesc['schemas']['Query']['properties']['corpus']['enum'])
|
||||
allowed_scopes = _getEnumValuesMinusUnspecified(v._rootDesc['schemas']['Query']['properties']['dataScope']['enum'])
|
||||
allowed_formats = _getEnumValuesMinusUnspecified(v._rootDesc['schemas']['MailExportOptions']['properties']['exportFormat']['enum'])
|
||||
export_format = 'MBOX'
|
||||
showConfidentialModeContent = None # default to not even set
|
||||
matterId = None
|
||||
body = {'query': {'dataScope': 'ALL_DATA'}, 'exportOptions': {}}
|
||||
i = 3
|
||||
@@ -8206,8 +8273,8 @@ def doCreateVaultExport():
|
||||
elif searchMethod == 'ORG_UNIT':
|
||||
body['query']['orgUnitInfo'] = {'orgUnitId': getOrgUnitId(sys.argv[i+1])[1]}
|
||||
i += 2
|
||||
elif searchMethod == 'TEAM_DRIVE':
|
||||
body['query']['teamDriveInfo'] = {'teamDriveIds': sys.argv[i+1].split(',')}
|
||||
elif searchMethod == 'SHARED_DRIVE':
|
||||
body['query']['sharedDriveInfo'] = {'sharedDriveIds': sys.argv[i+1].split(',')}
|
||||
i += 2
|
||||
elif searchMethod == 'ROOM':
|
||||
body['query']['hangoutsChatInfo'] = {'roomId': sys.argv[i+1].split(',')}
|
||||
@@ -8237,8 +8304,8 @@ def doCreateVaultExport():
|
||||
elif myarg in ['driveversiondate']:
|
||||
body['query'].setdefault('driveOptions', {})['versionDate'] = getDateZeroTimeOrFullTime(sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg in ['includeteamdrives']:
|
||||
body['query'].setdefault('driveOptions', {})['includeTeamDrives'] = getBoolean(sys.argv[i+1], myarg)
|
||||
elif myarg in ['includeshareddrives', 'includeteamdrives']:
|
||||
body['query'].setdefault('driveOptions', {})['includeSharedDrives'] = getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
elif myarg in ['includerooms']:
|
||||
body['query']['hangoutsChatOptions'] = {'includeRooms': getBoolean(sys.argv[i+1], myarg)}
|
||||
@@ -8249,6 +8316,15 @@ def doCreateVaultExport():
|
||||
print('ERROR: export format can be one of %s, got %s' % (', '.join(allowed_formats), export_format))
|
||||
sys.exit(3)
|
||||
i += 2
|
||||
elif myarg in ['showconfidentialmodecontent']:
|
||||
showConfidentialModeContent = getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
elif myarg in ['region']:
|
||||
allowed_regions = _getEnumValuesMinusUnspecified(v._rootDesc['schemas']['ExportOptions']['properties']['region']['enum'])
|
||||
body['exportOptions']['region'] = sys.argv[i+1].upper()
|
||||
if body['exportOptions']['region'] not in allowed_regions:
|
||||
systemErrorExit(3, 'region should be one of %s, got %s' % (', '.join(allowed_regions), body['exportOptions']['region']))
|
||||
i += 2
|
||||
elif myarg in ['includeaccessinfo']:
|
||||
body['exportOptions'].setdefault('driveOptions', {})['includeAccessInfo'] = getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
@@ -8272,6 +8348,8 @@ def doCreateVaultExport():
|
||||
if options_field:
|
||||
body['exportOptions'].pop('driveOptions', None)
|
||||
body['exportOptions'][options_field] = {'exportFormat': export_format}
|
||||
if showConfidentialModeContent is not None:
|
||||
body['exportOptions'][options_field]['showConfidentialModeContent'] = showConfidentialModeContent
|
||||
results = callGAPI(v.matters().exports(), 'create', matterId=matterId, body=body)
|
||||
print('Created export %s' % results['id'])
|
||||
print_json(None, results)
|
||||
@@ -9111,6 +9189,16 @@ def checkGroupExists(cd, group, i=0, count=0):
|
||||
entityUnknownWarning('Group', group, i, count)
|
||||
return None
|
||||
|
||||
def _checkMemberRoleIsSuspended(member, validRoles, isSuspended):
|
||||
if validRoles and member.get('role', ROLE_MEMBER) not in validRoles:
|
||||
return False
|
||||
if isSuspended is None:
|
||||
return True
|
||||
memberStatus = member.get('status', 'UNKNOWN')
|
||||
if not isSuspended:
|
||||
return memberStatus != 'SUSPENDED'
|
||||
return memberStatus == 'SUSPENDED'
|
||||
|
||||
UPDATE_GROUP_SUBCMDS = ['add', 'clear', 'delete', 'remove', 'sync', 'update']
|
||||
GROUP_ROLES_MAP = {
|
||||
'owner': ROLE_OWNER, 'owners': ROLE_OWNER,
|
||||
@@ -9315,12 +9403,7 @@ def doUpdateGroup():
|
||||
if not result:
|
||||
print('Group already has 0 members')
|
||||
return
|
||||
if checkSuspended is None:
|
||||
users_email = [member.get('email', member['id']) for member in result if not validRoles or member.get('role', ROLE_MEMBER) in validRoles]
|
||||
elif checkSuspended:
|
||||
users_email = [member.get('email', member['id']) for member in result if (not validRoles or member.get('role', ROLE_MEMBER) in validRoles) and member['status'] == 'SUSPENDED']
|
||||
else: # elif not checkSuspended
|
||||
users_email = [member.get('email', member['id']) for member in result if (not validRoles or member.get('role', ROLE_MEMBER) in validRoles) and member['status'] != 'SUSPENDED']
|
||||
users_email = [member.get('email', member['id']) for member in result if _checkMemberRoleIsSuspended(member, validRoles, checkSuspended)]
|
||||
if len(users_email) > 1:
|
||||
sys.stderr.write('Group: {0}, Will remove {1} {2}{3}s.\n'.format(group, len(users_email), '' if checkSuspended is None else ['Non-suspended ', 'Suspended '][checkSuspended], roles))
|
||||
for user_email in users_email:
|
||||
@@ -10340,6 +10423,9 @@ def doGetMobileInfo():
|
||||
info = callGAPI(cd.mobiledevices(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], resourceId=resourceId)
|
||||
if 'deviceId' in info:
|
||||
info['deviceId'] = info['deviceId'].encode('unicode-escape').decode(UTF8)
|
||||
attrib = 'securityPatchLevel'
|
||||
if attrib in info and int(info[attrib]):
|
||||
info[attrib] = utils.formatTimestampYMDHMS(info[attrib])
|
||||
print_json(None, info)
|
||||
|
||||
def print_json(object_name, object_value, spacing=''):
|
||||
@@ -10428,27 +10514,42 @@ def doSiteVerifyAttempt():
|
||||
print('ERROR: %s' % str(e))
|
||||
verify_data = callGAPI(verif.webResource(), 'getToken', body=body)
|
||||
print('Method: %s' % verify_data['method'])
|
||||
print('Token: %s' % verify_data['token'])
|
||||
print('Expected Token: %s' % verify_data['token'])
|
||||
if verify_data['method'] in ['DNS_CNAME', 'DNS_TXT']:
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = ['8.8.8.8', '8.8.4.4']
|
||||
simplehttp = _createHttpObj()
|
||||
base_url = 'https://dns.google/resolve?'
|
||||
query_params = {}
|
||||
if verify_data['method'] == 'DNS_CNAME':
|
||||
cname_token = verify_data['token']
|
||||
cname_list = cname_token.split(' ')
|
||||
cname_subdomain = cname_list[0]
|
||||
try:
|
||||
answers = resolver.query('%s.%s' % (cname_subdomain, a_domain), 'A')
|
||||
for answer in answers:
|
||||
print('DNS Record: %s' % answer)
|
||||
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||
print('ERROR: No such domain found in DNS!')
|
||||
query_params['name'] = '%s.%s' % (cname_subdomain, a_domain)
|
||||
query_params['type'] = 'cname'
|
||||
else:
|
||||
try:
|
||||
answers = resolver.query(a_domain, 'TXT')
|
||||
for answer in answers:
|
||||
print('DNS Record: %s' % str(answer).replace('"', ''))
|
||||
except dns.resolver.NXDOMAIN:
|
||||
print('ERROR: no such domain found in DNS!')
|
||||
query_params['name'] = a_domain
|
||||
query_params['type'] = 'txt'
|
||||
full_url = base_url + urlencode(query_params)
|
||||
(_, c) = simplehttp.request(full_url, 'GET')
|
||||
result = json.loads(c.decode(UTF8))
|
||||
status = result['Status']
|
||||
if status == 0 and 'Answer' in result:
|
||||
answers = result['Answer']
|
||||
if verify_data['method'] == 'DNS_CNAME':
|
||||
answer = answers[0]['data']
|
||||
else:
|
||||
answer = 'no matching record found'
|
||||
for possible_answer in answers:
|
||||
possible_answer['data'] = possible_answer['data'].strip('"')
|
||||
if possible_answer['data'].startswith('google-site-verification'):
|
||||
answer = possible_answer['data']
|
||||
break
|
||||
else:
|
||||
print('Unrelated TXT record: %s' % possible_answer['data'])
|
||||
print('Found DNS Record: %s' % answer)
|
||||
elif status == 0:
|
||||
systemErrorExit(1, 'DNS record not found')
|
||||
else:
|
||||
systemErrorExit(status, DNS_ERROR_CODES_MAP.get(status, 'Unknown error %s' % status))
|
||||
return
|
||||
print('SUCCESS!')
|
||||
print('Verified: %s' % verify_result['site']['identifier'])
|
||||
@@ -10600,11 +10701,11 @@ def doGetASPs(users):
|
||||
if asp['creationTime'] == '0':
|
||||
created_date = 'Unknown'
|
||||
else:
|
||||
created_date = datetime.datetime.fromtimestamp(int(asp['creationTime'])/1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
created_date = utils.formatTimestampYMDHMS(asp['creationTime'])
|
||||
if asp['lastTimeUsed'] == '0':
|
||||
used_date = 'Never'
|
||||
else:
|
||||
used_date = datetime.datetime.fromtimestamp(int(asp['lastTimeUsed'])/1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
used_date = utils.formatTimestampYMDHMS(asp['lastTimeUsed'])
|
||||
print(' ID: %s\n Name: %s\n Created: %s\n Last Used: %s\n' % (asp['codeId'], asp['name'], created_date, used_date))
|
||||
else:
|
||||
print(' no ASPs for %s\n' % user)
|
||||
@@ -10887,17 +10988,45 @@ def doDeleteOrg():
|
||||
print("Deleting organization %s" % name)
|
||||
callGAPI(cd.orgunits(), 'delete', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=encodeOrgUnitPath(makeOrgUnitPathRelative(name)))
|
||||
|
||||
# Send an email
|
||||
def send_email(msg_subj, msg_txt, msg_rcpt=None):
|
||||
userId, gmail = buildGmailGAPIObject(_getValueFromOAuth('email'))
|
||||
if not msg_rcpt:
|
||||
msg_rcpt = userId
|
||||
msg = MIMEText(msg_txt)
|
||||
msg['Subject'] = msg_subj
|
||||
msg['From'] = userId
|
||||
msg['To'] = msg_rcpt
|
||||
callGAPI(gmail.users().messages(), 'send',
|
||||
userId=userId, body={'raw': base64.urlsafe_b64encode(msg.as_bytes()).decode()})
|
||||
def send_email(subject, body, recipient=None, sender=None, user=None, method='send', labels=None, msgHeaders={}, kwargs={}):
|
||||
api_body = {}
|
||||
default_sender = default_recipient = False
|
||||
if not user:
|
||||
user = _getValueFromOAuth('email')
|
||||
userId, gmail = buildGmailGAPIObject(user)
|
||||
resource = gmail.users().messages()
|
||||
if labels:
|
||||
api_body['labelIds'] = labelsToLabelIds(gmail, labels)
|
||||
if not sender:
|
||||
sender = userId
|
||||
default_sender = True
|
||||
if not recipient:
|
||||
recipient = userId
|
||||
default_recipient = True
|
||||
msg = message_from_string(body)
|
||||
for header, value in msgHeaders.items():
|
||||
msg.__delitem__(header) # can remove multiple case-insensitive matching headers
|
||||
msg.add_header(header, value)
|
||||
if subject:
|
||||
msg.__delitem__('Subject')
|
||||
msg['Subject'] = subject
|
||||
if not default_sender:
|
||||
msg.__delitem__('From')
|
||||
if not msg['From']:
|
||||
msg['From'] = sender
|
||||
if not default_recipient:
|
||||
msg.__delitem__('to')
|
||||
if not msg['To']:
|
||||
msg['To'] = recipient
|
||||
api_body['raw'] = base64.urlsafe_b64encode(msg.as_bytes()).decode()
|
||||
if method == 'draft':
|
||||
resource = gmail.users().drafts()
|
||||
method = 'create'
|
||||
api_body = {'message': api_body}
|
||||
elif method in ['insert', 'import']:
|
||||
if method == 'import':
|
||||
method = 'import_'
|
||||
callGAPI(resource, method, userId=userId, body=api_body, **kwargs)
|
||||
|
||||
def addFieldToFieldsList(fieldName, fieldsChoiceMap, fieldsList):
|
||||
fields = fieldsChoiceMap[fieldName.lower()]
|
||||
@@ -10959,6 +11088,9 @@ def sortCSVTitles(firstTitle, titles):
|
||||
for title in restoreTitles[::-1]:
|
||||
titles.insert(0, title)
|
||||
|
||||
def QuotedArgumentList(items):
|
||||
return ' '.join([item if item and (item.find(' ') == -1) and (item.find(',') == -1) else '"'+item+'"' for item in items])
|
||||
|
||||
def writeCSVfile(csvRows, titles, list_type, todrive):
|
||||
def rowDateTimeFilterMatch(dateMode, rowDate, op, filterDate):
|
||||
if not rowDate or not isinstance(rowDate, str):
|
||||
@@ -11018,6 +11150,8 @@ def writeCSVfile(csvRows, titles, list_type, todrive):
|
||||
continue
|
||||
if filterVal[0] == 'regex':
|
||||
csvRows = [row for row in csvRows if filterVal[1].search(str(row.get(column, '')))]
|
||||
elif filterVal[0] == 'notregex':
|
||||
csvRows = [row for row in csvRows if not filterVal[1].search(str(row.get(column, '')))]
|
||||
elif filterVal[0] in ['date', 'time']:
|
||||
csvRows = [row for row in csvRows if rowDateTimeFilterMatch(filterVal[0] == 'date', row.get(column, ''), filterVal[1], filterVal[2])]
|
||||
elif filterVal[0] == 'count':
|
||||
@@ -11061,7 +11195,7 @@ and follow recommend steps to authorize GAM for Drive access.''' % (admin_email)
|
||||
mimeType = 'text/csv'
|
||||
else:
|
||||
mimeType = MIMETYPE_GA_SPREADSHEET
|
||||
body = {'description': ' '.join(sys.argv),
|
||||
body = {'description': QuotedArgumentList(sys.argv),
|
||||
'name': '%s - %s' % (GC_Values[GC_DOMAIN], list_type),
|
||||
'mimeType': mimeType}
|
||||
result = callGAPI(drive.files(), 'create', fields='webViewLink',
|
||||
@@ -11325,12 +11459,12 @@ def doPrintShowAlertFeedback():
|
||||
for feedbac in feedback:
|
||||
print(feedbac)
|
||||
|
||||
def _getValidAlertFeedbackTypes(ac):
|
||||
return [aftype for aftype in ac._rootDesc['schemas']['AlertFeedback']['properties']['type']['enum'] if aftype != 'ALERT_FEEDBACK_TYPE_UNSPECIFIED']
|
||||
def _getEnumValuesMinusUnspecified(values):
|
||||
return [a_type for a_type in values if '_UNSPECIFIED' not in a_type]
|
||||
|
||||
def doCreateAlertFeedback():
|
||||
_, ac = buildAlertCenterGAPIObject(_getValueFromOAuth('email'))
|
||||
valid_types = _getValidAlertFeedbackTypes(ac)
|
||||
valid_types = _getEnumValuesMinusUnspecified(ac._rootDesc['schemas']['AlertFeedback']['properties']['type']['enum'])
|
||||
alertId = sys.argv[3]
|
||||
body = {'type': sys.argv[4].upper()}
|
||||
if body['type'] not in valid_types:
|
||||
@@ -11809,8 +11943,7 @@ def doPrintGroupMembers():
|
||||
soft_errors=True,
|
||||
groupKey=group_email, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
|
||||
for member in group_members:
|
||||
if ((validRoles and member.get('role', ROLE_MEMBER) not in validRoles) or
|
||||
(checkSuspended is not None and ((not checkSuspended and member['status'] == 'SUSPENDED') or (checkSuspended and member['status'] != 'SUSPENDED')))):
|
||||
if not _checkMemberRoleIsSuspended(member, validRoles, checkSuspended):
|
||||
continue
|
||||
for title in member:
|
||||
if title not in titles:
|
||||
@@ -12039,10 +12172,12 @@ def doPrintMobileDevices():
|
||||
else:
|
||||
if attrib not in titles:
|
||||
titles.append(attrib)
|
||||
if attrib != 'deviceId':
|
||||
row[attrib] = mobile[attrib]
|
||||
else:
|
||||
if attrib == 'deviceId':
|
||||
row[attrib] = mobile[attrib].encode('unicode-escape').decode(UTF8)
|
||||
elif attrib == 'securityPatchLevel' and int(mobile[attrib]):
|
||||
row[attrib] = utils.formatTimestampYMDHMS(mobile[attrib])
|
||||
else:
|
||||
row[attrib] = mobile[attrib]
|
||||
csvRows.append(row)
|
||||
sortCSVTitles(['resourceId', 'deviceId', 'serialNumber', 'name', 'email', 'status'], titles)
|
||||
writeCSVfile(csvRows, titles, 'Mobile', todrive)
|
||||
@@ -12157,12 +12292,14 @@ def _checkTPMVulnerability(cros):
|
||||
cros['tpmVersionInfo']['tpmVulnerability'] = 'NOT IMPACTED'
|
||||
|
||||
def _guessAUE(cros, guessedAUEs):
|
||||
if not GC_Values.get('CROS_AUE_DATES', None):
|
||||
GC_Values['CROS_AUE_DATES'] = json.loads(getGAMProjectFile('src/cros-aue-dates.json'))
|
||||
crosModel = cros.get('model')
|
||||
if crosModel:
|
||||
if crosModel not in guessedAUEs:
|
||||
closest_match = difflib.get_close_matches(crosModel.lower(), CROS_AUE_DATES, n=1)
|
||||
closest_match = difflib.get_close_matches(crosModel.lower(), GC_Values['CROS_AUE_DATES'], n=1)
|
||||
if closest_match:
|
||||
guessedAUEs[crosModel] = {'guessedAUEDate': CROS_AUE_DATES[closest_match[0]],
|
||||
guessedAUEs[crosModel] = {'guessedAUEDate': GC_Values['CROS_AUE_DATES'][closest_match[0]],
|
||||
'guessedAUEModel': closest_match[0]}
|
||||
else:
|
||||
guessedAUEs[crosModel] = {'guessedAUEDate': u'',
|
||||
@@ -12608,12 +12745,16 @@ def doPrintResourceCalendars():
|
||||
fieldsTitles = {}
|
||||
titles = []
|
||||
csvRows = []
|
||||
query = None
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'query':
|
||||
query = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'allfields':
|
||||
fieldsList = []
|
||||
fieldsTitles = {}
|
||||
@@ -12636,7 +12777,8 @@ def doPrintResourceCalendars():
|
||||
page_message = 'Got %%total_items%% Resource Calendars: %%first_item%% - %%last_item%%\n'
|
||||
resources = callGAPIpages(cd.resources().calendars(), 'list', 'items',
|
||||
page_message=page_message, message_attribute='resourceId',
|
||||
customer=GC_Values[GC_CUSTOMER_ID], fields=fields)
|
||||
customer=GC_Values[GC_CUSTOMER_ID], query=query,
|
||||
fields=fields)
|
||||
for resource in resources:
|
||||
if 'featureInstances' in resource:
|
||||
resource['featureInstances'] = ','.join([a_feature['feature']['name'] for a_feature in resource.pop('featureInstances')])
|
||||
@@ -12698,9 +12840,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
groupKey=group, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
|
||||
users = []
|
||||
for member in members:
|
||||
if (((not groupUserMembersOnly) or (member['type'] == 'USER')) and
|
||||
(not validRoles or member.get('role', ROLE_MEMBER) in validRoles) and
|
||||
(checkSuspended is None or (not checkSuspended and member['status'] != 'SUSPENDED') or (checkSuspended and member['status'] == 'SUSPENDED'))):
|
||||
if ((not groupUserMembersOnly) or (member['type'] == 'USER')) and _checkMemberRoleIsSuspended(member, validRoles, checkSuspended):
|
||||
users.append(member.get('email', member['id']))
|
||||
elif entity_type in ['ou', 'org', 'ou_ns', 'org_ns', 'ou_susp', 'org_susp',]:
|
||||
if entity_type in ['ou_ns', 'org_ns']:
|
||||
@@ -14061,9 +14201,9 @@ def ProcessGAMCommand(args):
|
||||
elif command == 'calendar':
|
||||
argument = sys.argv[3].lower()
|
||||
if argument == 'showacl':
|
||||
doCalendarShowPrintACL(csvOut=False)
|
||||
doCalendarPrintShowACLs(False)
|
||||
elif argument == 'printacl':
|
||||
doCalendarShowPrintACL(csvOut=True)
|
||||
doCalendarPrintShowACLs(True)
|
||||
elif argument == 'add':
|
||||
doCalendarAddACL('Add')
|
||||
elif argument in ['del', 'delete']:
|
||||
@@ -14410,6 +14550,14 @@ def ProcessGAMCommand(args):
|
||||
elif command == 'imap':
|
||||
#doImap(users)
|
||||
runCmdForUsers(doImap, users, default_to_batch=True)
|
||||
elif command == 'sendemail':
|
||||
sendOrDropEmail(users, 'send')
|
||||
elif command == 'importemail':
|
||||
sendOrDropEmail(users, 'import')
|
||||
elif command == 'insertemail':
|
||||
sendOrDropEmail(users, 'insert')
|
||||
elif command == 'draftemail':
|
||||
sendOrDropEmail(users, 'draft')
|
||||
elif command == 'language':
|
||||
doLanguage(users)
|
||||
elif command in ['pop', 'pop3']:
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
python-dateutil
|
||||
dnspython
|
||||
google-api-python-client
|
||||
google-auth
|
||||
google-auth-httplib2
|
||||
google-auth-oauthlib
|
||||
google-auth-oauthlib==0.4.0
|
||||
httplib2>=0.13.0
|
||||
passlib
|
||||
|
||||
@@ -4,6 +4,8 @@ from xml.etree import ElementTree as ET
|
||||
import requests
|
||||
from html.parser import HTMLParser
|
||||
import string
|
||||
import sys
|
||||
import json
|
||||
import dateutil.parser
|
||||
|
||||
class MyHTMLParser(HTMLParser):
|
||||
@@ -27,18 +29,18 @@ class MyHTMLParser(HTMLParser):
|
||||
fullname = '%s %s' % (oem, model)
|
||||
fullname = fullname.lower()
|
||||
date = dateutil.parser.parse(data).replace(day=1).strftime('%Y-%m-%dT00:00:00.000Z')
|
||||
output_rows.append(" '%s': '%s'," % (fullname, date))
|
||||
output_rows[fullname] = date
|
||||
if fullname in exceptions:
|
||||
for value in exceptions[fullname]:
|
||||
output_rows.append(" '%s': '%s'," % (value, date))
|
||||
output_rows[value] = date
|
||||
data_is_date = False
|
||||
else:
|
||||
model = ''.join(filter(lambda x: x in printable, data)).replace('"', '\\"')
|
||||
model = ''.join(filter(lambda x: x in printable, data))
|
||||
data_is_date = True
|
||||
next_data_is_td = False
|
||||
|
||||
global oem, next_data_is_oem, next_data_is_td, data_is_date, model, printable, exceptions, output_rows
|
||||
output_rows = []
|
||||
output_rows = {}
|
||||
printable = set(string.printable)
|
||||
exceptions = {
|
||||
# 'AUE OEM MODEL': ['API MODEL 1', ...]
|
||||
@@ -95,10 +97,6 @@ next_data_is_oem = False
|
||||
next_data_is_td = False
|
||||
data_is_date = False
|
||||
auepage = requests.get('https://support.google.com/chrome/a/answer/6220366?hl=en')
|
||||
print('CROS_AUE_DATES = {')
|
||||
parser = MyHTMLParser()
|
||||
parser.feed(auepage.content.decode('utf-8'))
|
||||
output_rows.sort(key=str.lower)
|
||||
for row in output_rows:
|
||||
print(row)
|
||||
print('}')
|
||||
print(json.dumps(output_rows, indent=2, sort_keys=True))
|
||||
|
||||
@@ -2,22 +2,22 @@ echo "Installing Net-Framework-Core..."
|
||||
export mypath=$(pwd)
|
||||
until powershell Install-WindowsFeature Net-Framework-Core; do echo "trying again..."; done
|
||||
cd ~/pybuild
|
||||
export exefile=Win32OpenSSL_Light-${BUILD_OPENSSL_VERSION//./_}.exe
|
||||
if [ ! -e $exefile ]; then
|
||||
echo "Downloading $exefile..."
|
||||
wget --quiet https://slproweb.com/download/$exefile
|
||||
fi
|
||||
echo "Installing $exefile..."
|
||||
powershell ".\\${exefile} /silent /sp- /suppressmsgboxes /DIR=C:\\ssl"
|
||||
#export exefile=Win32OpenSSL_Light-${BUILD_OPENSSL_VERSION//./_}.exe
|
||||
#if [ ! -e $exefile ]; then
|
||||
# echo "Downloading $exefile..."
|
||||
# wget --quiet https://slproweb.com/download/$exefile
|
||||
#fi
|
||||
#echo "Installing $exefile..."
|
||||
#powershell ".\\${exefile} /silent /sp- /suppressmsgboxes /DIR=C:\\ssl"
|
||||
cinst -y --forcex86 python3
|
||||
until cinst -y wixtoolset; do echo "trying again..."; done
|
||||
echo "OpenSSL dlls..."
|
||||
ls -alRF /c/ssl
|
||||
echo "c drive"
|
||||
ls -al /c/
|
||||
echo "Python dlls..."
|
||||
ls -al /c/Python37/DLLs
|
||||
until cp -v /c/ssl/*.dll /c/Python37/DLLs; do echo "trying again..."; done
|
||||
#echo "OpenSSL dlls..."
|
||||
#ls -alRF /c/ssl
|
||||
#echo "c drive"
|
||||
#ls -al /c/
|
||||
#echo "Python dlls..."
|
||||
#ls -al /c/Python37/DLLs
|
||||
#until cp -v /c/ssl/*.dll /c/Python37/DLLs; do echo "trying again..."; done
|
||||
export PATH=$PATH:/c/Python37/scripts
|
||||
cd $mypath
|
||||
pip install --upgrade pip
|
||||
|
||||
@@ -2,22 +2,22 @@ echo "Installing Net-Framework-Core..."
|
||||
export mypath=$(pwd)
|
||||
until powershell Install-WindowsFeature Net-Framework-Core; do echo "trying again..."; done
|
||||
cd ~/pybuild
|
||||
export exefile=Win64OpenSSL_Light-${BUILD_OPENSSL_VERSION//./_}.exe
|
||||
if [ ! -e $exefile ]; then
|
||||
echo "Downloading $exefile..."
|
||||
wget --quiet https://slproweb.com/download/$exefile
|
||||
fi
|
||||
echo "Installing $exefile..."
|
||||
powershell ".\\${exefile} /silent /sp- /suppressmsgboxes /DIR=C:\\ssl"
|
||||
#export exefile=Win64OpenSSL_Light-${BUILD_OPENSSL_VERSION//./_}.exe
|
||||
#if [ ! -e $exefile ]; then
|
||||
# echo "Downloading $exefile..."
|
||||
# wget --quiet https://slproweb.com/download/$exefile
|
||||
#fi
|
||||
#echo "Installing $exefile..."
|
||||
#powershell ".\\${exefile} /silent /sp- /suppressmsgboxes /DIR=C:\\ssl"
|
||||
cinst -y python3
|
||||
until cinst -y wixtoolset; do echo "trying again..."; done
|
||||
echo "OpenSSL dlls..."
|
||||
ls -alRF /c/ssl
|
||||
echo "c drive"
|
||||
ls -al /c
|
||||
echo "Python dlls..."
|
||||
ls -al /c/Python37/DLLs
|
||||
until cp -v /c/ssl/*.dll /c/Python37/DLLs; do echo "trying again..."; done
|
||||
#echo "OpenSSL dlls..."
|
||||
#ls -alRF /c/ssl
|
||||
#echo "c drive"
|
||||
#ls -al /c
|
||||
#echo "Python dlls..."
|
||||
#ls -al /c/Python37/DLLs
|
||||
#until cp -v /c/ssl/*.dll /c/Python37/DLLs; do echo "trying again..."; done
|
||||
export PATH=$PATH:/c/Python37/scripts
|
||||
cd $mypath
|
||||
pip install --upgrade pip
|
||||
|
||||
14
src/utils.py
14
src/utils.py
@@ -1,3 +1,4 @@
|
||||
import datetime
|
||||
import re
|
||||
import sys
|
||||
from html.entities import name2codepoint
|
||||
@@ -22,8 +23,7 @@ class _DeHTMLParser(HTMLParser):
|
||||
self.__text.append(data)
|
||||
|
||||
def handle_charref(self, name):
|
||||
self.__text.append(
|
||||
chr(int(name[1:], 16)) if name.startswith('x') else chr(int(name)))
|
||||
self.__text.append(chr(int(name[1:], 16)) if name.startswith('x') else chr(int(name)))
|
||||
|
||||
def handle_entityref(self, name):
|
||||
cp = name2codepoint.get(name)
|
||||
@@ -73,6 +73,16 @@ def indentMultiLineText(message, n=0):
|
||||
return message.replace('\n', '\n{0}'.format(' ' * n)).rstrip()
|
||||
|
||||
|
||||
def formatTimestampYMD(timestamp):
|
||||
return datetime.datetime.fromtimestamp(int(timestamp)/1000).strftime('%Y-%m-%d')
|
||||
|
||||
def formatTimestampYMDHMS(timestamp):
|
||||
return datetime.datetime.fromtimestamp(int(timestamp)/1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def formatTimestampYMDHMSF(timestamp):
|
||||
return str(datetime.datetime.fromtimestamp(int(timestamp)/1000))
|
||||
|
||||
|
||||
def formatFileSize(fileSize):
|
||||
if fileSize == 0:
|
||||
return '0kb'
|
||||
|
||||
276
src/var.py
276
src/var.py
@@ -6,7 +6,7 @@ import platform
|
||||
import re
|
||||
|
||||
gam_author = 'Jay Lee <jay0lee@gmail.com>'
|
||||
gam_version = '4.87'
|
||||
gam_version = '4.89'
|
||||
gam_license = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
GAM_URL = 'https://git.io/gam'
|
||||
@@ -20,7 +20,7 @@ GAM_RELEASES = 'https://github.com/jay0lee/GAM/releases'
|
||||
GAM_WIKI = 'https://github.com/jay0lee/GAM/wiki'
|
||||
GAM_ALL_RELEASES = 'https://api.github.com/repos/jay0lee/GAM/releases'
|
||||
GAM_LATEST_RELEASE = GAM_ALL_RELEASES+'/latest'
|
||||
GAM_PROJECT_APIS = 'https://raw.githubusercontent.com/jay0lee/GAM/master/src/project-apis.txt'
|
||||
GAM_PROJECT_FILEPATH = 'https://raw.githubusercontent.com/jay0lee/GAM/master/'
|
||||
|
||||
true_values = ['on', 'yes', 'enabled', 'true', '1']
|
||||
false_values = ['off', 'no', 'disabled', 'false', '0']
|
||||
@@ -430,6 +430,18 @@ DOCUMENT_FORMATS_MAP = {
|
||||
{'mime': 'application/vnd.oasis.opendocument.text', 'ext': '.odt'}],
|
||||
}
|
||||
|
||||
DNS_ERROR_CODES_MAP = {
|
||||
1: 'DNS Query Format Error',
|
||||
2: 'Server failed to complete the DNS request',
|
||||
3: 'Domain name does not exist',
|
||||
4: 'Function not implemented',
|
||||
5: 'The server refused to answer for the query',
|
||||
6: 'Name that should not exist, does exist',
|
||||
7: 'RRset that should not exist, does exist',
|
||||
8: 'Server not authoritative for the zone',
|
||||
9: 'Name not in zone'
|
||||
}
|
||||
|
||||
EMAILSETTINGS_OLD_NEW_OLD_FORWARD_ACTION_MAP = {
|
||||
'ARCHIVE': 'archive',
|
||||
'DELETE': 'trash',
|
||||
@@ -600,266 +612,6 @@ CROS_END_ARGUMENTS = ['end', 'enddate']
|
||||
CROS_TPM_VULN_VERSIONS = ['41f', '420', '628', '8520',]
|
||||
CROS_TPM_FIXED_VERSIONS = ['422', '62b', '8521',]
|
||||
|
||||
# parsed from https://support.google.com/chrome/a/answer/6220366?hl=en
|
||||
# using src/tools/parse-aue.py
|
||||
CROS_AUE_DATES = {
|
||||
'acer ac700': '2016-08-01T00:00:00.000Z',
|
||||
'acer c7 chromebook (c710)': '2017-10-01T00:00:00.000Z',
|
||||
'acer c7 chromebook': '2017-10-01T00:00:00.000Z',
|
||||
'acer c720 chromebook': '2019-06-01T00:00:00.000Z',
|
||||
'acer c740 chromebook': '2019-06-01T00:00:00.000Z',
|
||||
'acer chromebase 24': '2021-06-01T00:00:00.000Z',
|
||||
'acer chromebase': '2020-08-01T00:00:00.000Z',
|
||||
'acer chromebook 11 (c720, c720p)': '2019-06-01T00:00:00.000Z',
|
||||
'acer chromebook 11 (c732, c732t, c732l, c732lt)': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook 11 (c740)': '2020-06-01T00:00:00.000Z',
|
||||
'acer chromebook 11 (c771, c771t)': '2022-11-01T00:00:00.000Z',
|
||||
'acer chromebook 11 (cb3-111, c730, c730e)': '2019-08-01T00:00:00.000Z',
|
||||
'acer chromebook 11 (cb3-131, c735)': '2021-01-01T00:00:00.000Z',
|
||||
'acer chromebook 11 (cb311-8h, cb311-8ht)': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook 11 n7 (c731, c731t)': '2022-01-01T00:00:00.000Z',
|
||||
'acer chromebook 13 (cb5-311)': '2019-09-01T00:00:00.000Z',
|
||||
'acer chromebook 13 (cb713-1w)': '2024-06-01T00:00:00.000Z',
|
||||
'acer chromebook 13(cb5-311, c810)': '2019-09-01T00:00:00.000Z',
|
||||
'acer chromebook 14 (cb3-431)': '2021-06-01T00:00:00.000Z',
|
||||
'acer chromebook 14 for work (cp5-471)': '2022-11-01T00:00:00.000Z',
|
||||
'acer chromebook 15 (c910 / cb5-571)': '2020-06-01T00:00:00.000Z',
|
||||
'acer chromebook 15 (cb3-531)': '2020-06-01T00:00:00.000Z',
|
||||
'acer chromebook 15 (cb3-532)': '2021-08-01T00:00:00.000Z',
|
||||
'acer chromebook 15 (cb315-1h,cb315-1ht)': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook 15 (cb5-571, c910)': '2020-06-01T00:00:00.000Z',
|
||||
'acer chromebook 15 (cb515-1h,cb515-1ht)': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook 311 (c721, c733, c733u, c733t)': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook 311': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook 311': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook 315 (cb315-2h)': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook 315': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook 512 (c851, c851t)': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook 514': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook 714 (cb714-1w / cb714-1wt)': '2024-06-01T00:00:00.000Z',
|
||||
'acer chromebook 715 (cb715-1w / cb715-1wt)': '2024-06-01T00:00:00.000Z',
|
||||
'acer chromebook r11 (cb5-132t, c738t)': '2021-06-01T00:00:00.000Z',
|
||||
'acer chromebook r13 (cb5-312t)': '2021-09-01T00:00:00.000Z',
|
||||
'acer chromebook spin 11 (cp311-h1, cp311-1hn)': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook spin 11 (r751t)': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook spin 13 (cp713-1wn)': '2024-06-01T00:00:00.000Z',
|
||||
'acer chromebook spin 15 (cp315)': '2023-11-01T00:00:00.000Z',
|
||||
'acer chromebook spin 311 (r721t)': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook spin 511 (r752t, r752tn)': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook spin 511': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook spin 512 (r851tn)': '2025-06-01T00:00:00.000Z',
|
||||
'acer chromebook tab 10': '2023-08-01T00:00:00.000Z',
|
||||
'acer chromebox cxi2 / cxv2': '2020-06-01T00:00:00.000Z',
|
||||
'acer chromebox cxi2': '2020-06-01T00:00:00.000Z',
|
||||
'acer chromebox cxi3': '2024-06-01T00:00:00.000Z',
|
||||
'acer chromebox': '2019-09-01T00:00:00.000Z',
|
||||
'aopen chromebase commercial': '2020-09-01T00:00:00.000Z',
|
||||
'aopen chromebase mini': '2022-02-01T00:00:00.000Z',
|
||||
'aopen chromebox commercial 2': '2024-06-01T00:00:00.000Z',
|
||||
'aopen chromebox commercial': '2020-09-01T00:00:00.000Z',
|
||||
'aopen chromebox mini': '2022-02-01T00:00:00.000Z',
|
||||
'asi chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'asus chromebit cs10': '2020-11-01T00:00:00.000Z',
|
||||
'asus chromebook c200': '2019-06-01T00:00:00.000Z',
|
||||
'asus chromebook c200ma': '2019-06-01T00:00:00.000Z',
|
||||
'asus chromebook c201pa': '2020-06-01T00:00:00.000Z',
|
||||
'asus chromebook c201pa': '2020-06-01T00:00:00.000Z',
|
||||
'asus chromebook c202sa': '2021-06-01T00:00:00.000Z',
|
||||
'asus chromebook c204': '2025-06-01T00:00:00.000Z',
|
||||
'asus chromebook c204': '2025-06-01T00:00:00.000Z',
|
||||
'asus chromebook c213na': '2023-11-01T00:00:00.000Z',
|
||||
'asus chromebook c223': '2023-11-01T00:00:00.000Z',
|
||||
'asus chromebook c300': '2019-08-01T00:00:00.000Z',
|
||||
'asus chromebook c300ma': '2019-08-01T00:00:00.000Z',
|
||||
'asus chromebook c300sa / c301sa': '2021-06-01T00:00:00.000Z',
|
||||
'asus chromebook c403': '2023-11-01T00:00:00.000Z',
|
||||
'asus chromebook c423': '2023-11-01T00:00:00.000Z',
|
||||
'asus chromebook c523': '2023-11-01T00:00:00.000Z',
|
||||
'asus chromebook flip c100pa': '2020-07-01T00:00:00.000Z',
|
||||
'asus chromebook flip c101pa': '2023-08-01T00:00:00.000Z',
|
||||
'asus chromebook flip c213': '2023-11-01T00:00:00.000Z',
|
||||
'asus chromebook flip c214': '2025-06-01T00:00:00.000Z',
|
||||
'asus chromebook flip c302': '2022-11-01T00:00:00.000Z',
|
||||
'asus chromebook flip c434': '2024-06-01T00:00:00.000Z',
|
||||
'asus chromebook tablet ct100': '2023-08-01T00:00:00.000Z',
|
||||
'asus chromebox (cn60)': '2019-09-01T00:00:00.000Z',
|
||||
'asus chromebox 2 (cn62)': '2021-06-01T00:00:00.000Z',
|
||||
'asus chromebox 3 (cn65)': '2024-06-01T00:00:00.000Z',
|
||||
'asus chromebox 3': '2024-06-01T00:00:00.000Z',
|
||||
'asus chromebox cn60': '2019-09-01T00:00:00.000Z',
|
||||
'asus chromebox cn62': '2021-06-01T00:00:00.000Z',
|
||||
'bobicus chromebook 11': '2020-06-01T00:00:00.000Z',
|
||||
'chromebook 11 (c730 / cb3-111)': '2019-08-01T00:00:00.000Z',
|
||||
'chromebook 11 (c735)': '2021-01-01T00:00:00.000Z',
|
||||
'chromebook 15 (cb515 - 1ht / 1h)': '2023-11-01T00:00:00.000Z',
|
||||
'chromebook 311 (c721)': '2025-06-01T00:00:00.000Z',
|
||||
'chromebook pcm-116e': '2020-06-01T00:00:00.000Z',
|
||||
'consumer chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'cr-48': '2015-12-01T00:00:00.000Z',
|
||||
'crambo chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'ctl chromebook j41 / j41t': '2023-11-01T00:00:00.000Z',
|
||||
'ctl chromebook nl7 / nl7t-360 / nl7tw-360': '2023-11-01T00:00:00.000Z',
|
||||
'ctl chromebook nl7': '2023-11-01T00:00:00.000Z',
|
||||
'ctl chromebook tab tx1': '2023-08-01T00:00:00.000Z',
|
||||
'ctl chromebook tablet tx1 for education': '2023-08-01T00:00:00.000Z',
|
||||
'ctl chromebox cbx1': '2024-06-01T00:00:00.000Z',
|
||||
'ctl j2 / j4 chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'ctl j5 chromebook': '2021-08-01T00:00:00.000Z',
|
||||
'ctl n6 education chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'ctl nl61 chromebook': '2021-08-01T00:00:00.000Z',
|
||||
'dell chromebook 11 (3120)': '2020-06-01T00:00:00.000Z',
|
||||
'dell chromebook 11 (3180)': '2022-05-01T00:00:00.000Z',
|
||||
'dell chromebook 11 (5190)': '2023-11-01T00:00:00.000Z',
|
||||
'dell chromebook 11 2-in-1 (3189)': '2022-05-01T00:00:00.000Z',
|
||||
'dell chromebook 11 2-in-1 (5190)': '2023-11-01T00:00:00.000Z',
|
||||
'dell chromebook 11': '2019-06-01T00:00:00.000Z',
|
||||
'dell chromebook 13 (3380)': '2022-11-01T00:00:00.000Z',
|
||||
'dell chromebook 13 (7310)': '2020-09-01T00:00:00.000Z',
|
||||
'dell chromebook 3100 2-in-1': '2025-06-01T00:00:00.000Z',
|
||||
'dell chromebook 3100': '2025-06-01T00:00:00.000Z',
|
||||
'dell chromebook 3400': '2025-06-01T00:00:00.000Z',
|
||||
'dell chromebox': '2019-09-01T00:00:00.000Z',
|
||||
'dell inspiron chromebook 14 2-in-1 (7486)': '2024-06-01T00:00:00.000Z',
|
||||
'edugear chromebook k': '2020-06-01T00:00:00.000Z',
|
||||
'edugear chromebook m': '2020-06-01T00:00:00.000Z',
|
||||
'edugear chromebook r': '2020-06-01T00:00:00.000Z',
|
||||
'edugear cmt chromebook': '2021-08-01T00:00:00.000Z',
|
||||
'edxis chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'edxis education chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'epik 11.6" chromebook elb1101': '2020-06-01T00:00:00.000Z',
|
||||
'google chromebook pixel (2015)': '2020-06-01T00:00:00.000Z',
|
||||
'google chromebook pixel': '2018-06-01T00:00:00.000Z',
|
||||
'google cr-48': '2015-12-01T00:00:00.000Z',
|
||||
'google pixel slate': '2024-06-01T00:00:00.000Z',
|
||||
'google pixelbook': '2024-06-01T00:00:00.000Z',
|
||||
'haier chromebook 11 c': '2021-08-01T00:00:00.000Z',
|
||||
'haier chromebook 11 g2': '2020-09-01T00:00:00.000Z',
|
||||
'haier chromebook 11': '2020-06-01T00:00:00.000Z',
|
||||
'haier chromebook 11e': '2020-06-01T00:00:00.000Z',
|
||||
'hexa chromebook pi': '2020-06-01T00:00:00.000Z',
|
||||
'hisense chromebook 11': '2020-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11 1100-1199 / hp chromebook 11 g1': '2018-10-01T00:00:00.000Z',
|
||||
'hp chromebook 11 2000-2099 / hp chromebook 11 g2': '2019-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11 2100-2199 / hp chromebook 11 g3': '2020-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11 2200-2299 / hp chromebook 11 g4/g4 ee': '2020-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g1': '2018-10-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g2': '2019-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g3': '2020-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g4/g4 ee': '2020-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g5 / hp chromebook 11-vxxx': '2021-07-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g5 ee': '2022-01-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g5': '2021-07-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g6 ee': '2023-11-01T00:00:00.000Z',
|
||||
'hp chromebook 11 g7 ee': '2025-06-01T00:00:00.000Z',
|
||||
'hp chromebook 11a g6 ee': '2025-06-01T00:00:00.000Z',
|
||||
'hp chromebook 13 g1': '2022-11-01T00:00:00.000Z',
|
||||
'hp chromebook 14 / hp chromebook 14 g5': '2023-11-01T00:00:00.000Z',
|
||||
'hp chromebook 14 ak000-099 / hp chromebook 14 g4': '2021-09-01T00:00:00.000Z',
|
||||
'hp chromebook 14 db0000-db0999': '2025-06-01T00:00:00.000Z',
|
||||
'hp chromebook 14 g3': '2019-10-01T00:00:00.000Z',
|
||||
'hp chromebook 14 g4': '2021-09-01T00:00:00.000Z',
|
||||
'hp chromebook 14 g5': '2023-11-01T00:00:00.000Z',
|
||||
'hp chromebook 14 x000-x999 / hp chromebook 14 g3': '2019-10-01T00:00:00.000Z',
|
||||
'hp chromebook 14': '2019-06-01T00:00:00.000Z',
|
||||
'hp chromebook 14a g5': '2025-06-01T00:00:00.000Z',
|
||||
'hp chromebook 15 g1': '2024-06-01T00:00:00.000Z',
|
||||
'hp chromebook x2 ': '2024-06-01T00:00:00.000Z',
|
||||
'hp chromebook x360 11 g1 ee': '2023-11-01T00:00:00.000Z',
|
||||
'hp chromebook x360 11 g2 ee': '2025-06-01T00:00:00.000Z',
|
||||
'hp chromebook x360 14 g1': '2024-06-01T00:00:00.000Z',
|
||||
'hp chromebook x360 14': '2024-06-01T00:00:00.000Z',
|
||||
'hp chromebox cb1-(000-099) / hp chromebox g1/ hp chromebox for meetings': '2019-09-01T00:00:00.000Z',
|
||||
'hp chromebox g1': '2019-09-01T00:00:00.000Z',
|
||||
'hp chromebox g2': '2024-06-01T00:00:00.000Z',
|
||||
'hp pavilion chromebook 14': '2018-02-01T00:00:00.000Z',
|
||||
'jp sa couto chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'lava xolo chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'lenovo 100e chromebook 2nd gen mtk': '2025-06-01T00:00:00.000Z',
|
||||
'lenovo 100e chromebook 2nd gen': '2025-06-01T00:00:00.000Z',
|
||||
'lenovo 100e chromebook': '2023-11-01T00:00:00.000Z',
|
||||
'lenovo 100s chromebook': '2020-09-01T00:00:00.000Z',
|
||||
'lenovo 14e chromebook': '2025-06-01T00:00:00.000Z',
|
||||
'lenovo 300e chromebook 2nd gen mtk': '2025-06-01T00:00:00.000Z',
|
||||
'lenovo 300e chromebook 2nd gen': '2025-06-01T00:00:00.000Z',
|
||||
'lenovo 300e chromebook': '2025-06-01T00:00:00.000Z',
|
||||
'lenovo 500e chromebook 2nd gen': '2025-06-01T00:00:00.000Z',
|
||||
'lenovo 500e chromebook': '2023-11-01T00:00:00.000Z',
|
||||
'lenovo chromebook c330': '2022-06-01T00:00:00.000Z',
|
||||
'lenovo chromebook s330': '2022-06-01T00:00:00.000Z',
|
||||
'lenovo flex 11 chromebook': '2022-06-01T00:00:00.000Z',
|
||||
'lenovo ideapad c330 chromebook': '2022-06-01T00:00:00.000Z',
|
||||
'lenovo ideapad s330 chromebook': '2022-06-01T00:00:00.000Z',
|
||||
'lenovo n20 chromebook': '2019-06-01T00:00:00.000Z',
|
||||
'lenovo n21 chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'lenovo n22 chromebook': '2021-06-01T00:00:00.000Z',
|
||||
'lenovo n23 chromebook': '2021-06-01T00:00:00.000Z',
|
||||
'lenovo n23 yoga chromebook': '2022-06-01T00:00:00.000Z',
|
||||
'lenovo n42 chromebook': '2021-06-01T00:00:00.000Z',
|
||||
'lenovo thinkcentre chromebox': '2020-06-01T00:00:00.000Z',
|
||||
'lenovo thinkpad 11e 3rd gen chromebook': '2021-06-01T00:00:00.000Z',
|
||||
'lenovo thinkpad 11e 4th gen chromebook': '2023-11-01T00:00:00.000Z',
|
||||
'lenovo thinkpad 11e chromebook (4th gen)/lenovo thinkpad yoga 11e chromebook (4th gen)': '2023-11-01T00:00:00.000Z',
|
||||
'lenovo thinkpad 11e chromebook': '2019-06-01T00:00:00.000Z',
|
||||
'lenovo thinkpad 13': '2022-11-01T00:00:00.000Z',
|
||||
'lenovo thinkpad x131e chromebook': '2018-06-01T00:00:00.000Z',
|
||||
'lenovo yoga c630 chromebook': '2024-06-01T00:00:00.000Z',
|
||||
'lg chromebase (22cb25s)': '2020-06-01T00:00:00.000Z',
|
||||
'lg chromebase (22cv241)': '2019-06-01T00:00:00.000Z',
|
||||
'lumos education chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'm&a chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'mecer chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'mecer v2 chromebook': '2021-08-01T00:00:00.000Z',
|
||||
'medion chromebook akoya s2013 ': '2020-06-01T00:00:00.000Z',
|
||||
'medion chromebook s2015': '2020-06-01T00:00:00.000Z',
|
||||
'multilaser chromebook m11c': '2021-08-01T00:00:00.000Z',
|
||||
'ncomputing chromebook cx100': '2020-06-01T00:00:00.000Z',
|
||||
'ncomputing chromebook cx110': '2020-06-01T00:00:00.000Z',
|
||||
'nexian chromebook 11.6\"': '2020-06-01T00:00:00.000Z',
|
||||
'pcmerge chromebook al116': '2023-11-01T00:00:00.000Z',
|
||||
'pcmerge chromebookpcm-116e/pcm-116eb': '2020-06-01T00:00:00.000Z',
|
||||
'pcmerge chromebookpcm-116t-432b': '2021-08-01T00:00:00.000Z',
|
||||
'poin2 chromebook 11': '2020-06-01T00:00:00.000Z',
|
||||
'poin2 chromebook 11c': '2022-03-01T00:00:00.000Z',
|
||||
'poin2 chromebook 14': '2022-03-01T00:00:00.000Z',
|
||||
'positivo chromebook c216b': '2021-08-01T00:00:00.000Z',
|
||||
'positivo chromebook ch1190': '2020-06-01T00:00:00.000Z',
|
||||
'prowise 11.6" entry line chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'prowise chromebook eduline': '2023-11-01T00:00:00.000Z',
|
||||
'prowise chromebook entryline': '2020-06-01T00:00:00.000Z',
|
||||
'prowise chromebook proline': '2021-08-01T00:00:00.000Z',
|
||||
'prowise proline chromebook': '2021-08-01T00:00:00.000Z',
|
||||
'rgs education chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'samsung chromebook - xe303': '2018-07-01T00:00:00.000Z',
|
||||
'samsung chromebook 2 11\" - xe500c12': '2020-06-01T00:00:00.000Z',
|
||||
'samsung chromebook 2 11\"': '2019-06-01T00:00:00.000Z',
|
||||
'samsung chromebook 2 13\"': '2019-06-01T00:00:00.000Z',
|
||||
'samsung chromebook 3': '2021-06-01T00:00:00.000Z',
|
||||
'samsung chromebook plus (v2)': '2024-06-01T00:00:00.000Z',
|
||||
'samsung chromebook plus': '2023-08-01T00:00:00.000Z',
|
||||
'samsung chromebook pro': '2022-11-01T00:00:00.000Z',
|
||||
'samsung chromebook series 5 550': '2017-05-01T00:00:00.000Z',
|
||||
'samsung chromebook series 5': '2016-06-01T00:00:00.000Z',
|
||||
'samsung chromebook': '2018-07-01T00:00:00.000Z',
|
||||
'samsung chromebox series 3': '2018-03-01T00:00:00.000Z',
|
||||
'sector 5 e1 rugged chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'sector 5 e3 chromebook': '2023-11-01T00:00:00.000Z',
|
||||
'senkatel c1101 chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'thinkpad 11e chromebook 3rd gen (yoga/clamshell)': '2021-06-01T00:00:00.000Z',
|
||||
'thinkpad 13 chromebook': '2022-11-01T00:00:00.000Z',
|
||||
'toshiba chromebook 2 (2015 edition)': '2020-09-01T00:00:00.000Z',
|
||||
'toshiba chromebook 2': '2020-06-01T00:00:00.000Z',
|
||||
'toshiba chromebook': '2019-06-01T00:00:00.000Z',
|
||||
'true idc chromebook 11': '2020-06-01T00:00:00.000Z',
|
||||
'true idc chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'videonet chromebook bl10': '2020-06-01T00:00:00.000Z',
|
||||
'videonet chromebook': '2020-06-01T00:00:00.000Z',
|
||||
'viewsonic nmp660 chromebox': '2024-06-01T00:00:00.000Z',
|
||||
'viglen chromebook 11': '2020-06-01T00:00:00.000Z',
|
||||
'viglen chromebook 11c': '2023-11-01T00:00:00.000Z',
|
||||
'viglen chromebook 360': '2021-08-01T00:00:00.000Z',
|
||||
'xolo chromebook': '2020-06-01T00:00:00.000Z',
|
||||
}
|
||||
|
||||
COLLABORATIVE_ACL_CHOICES = {
|
||||
'members': 'ALL_MEMBERS',
|
||||
'managersonly': 'MANAGERS_ONLY',
|
||||
|
||||
Reference in New Issue
Block a user