forked from polo2ro/imapbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dsn.py
171 lines (131 loc) · 5.67 KB
/
dsn.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import urllib
from utilities import errorHandler
# DSN:
# defaults to INBOX, path represents a single folder:
# imap://username:password@imap.gmail.com:993/
# imap://username:password@imap.gmail.com:993/INBOX
#
# get all folders
# imap://username:password@imap.gmail.com:993/__ALL__
#
# singe folder with ssl, both are the same:
# imaps://username:password@imap.gmail.com:993/INBOX
# imap://username:password@imap.gmail.com:993/INBOX?ssl=true
#
# folder as provided as path or as query param "remote_folder" with comma separated list
# imap://username:password@imap.gmail.com:993/INBOX.Drafts
# imap://username:password@imap.gmail.com:993/?remote_folder=INBOX.Drafts
#
# combined list of folders with path and ?remote_folder
# imap://username:password@imap.gmail.com:993/INBOX.Drafts?remote_folder=INBOX.Sent
#
# with multiple remote_folder:
# imap://username:password@imap.gmail.com:993/?remote_folder=INBOX.Drafts
# imap://username:password@imap.gmail.com:993/?remote_folder=INBOX.Drafts,INBOX.Sent
#
# setting other parameters
# imap://username:password@imap.gmail.com:993/?name=Account1
def get_account(dsn, name=None):
"""
Parse a DSN string and return a dictionary of account parameters.
The DSN string should be in the form of:
"imap[s]://username:password@server.tld:993/INBOX.Drafts,INBOX.Sent?remote_folder=INBOX.Drafts,More.Folders?ssl=true&name=Account1"
The function will return a dictionary with the following keys:
- name: The name of the account
- host: The hostname of the IMAP server
- port: The port number of the IMAP server
- username: The username to login with
- password: The password to login with
- remote_folder: A string containing a comma separated list of folders to archive
- ssl: A boolean indicating whether to use SSL or not
The function will also set the name, either by the name parameter provided or generate one of the username and host.
"""
account = {
'name': 'account', # this i different to imapbox.py
'host': None,
'port': 993,
'username': None,
'password': None,
'remote_folder': 'INBOX',
'exclude_folder': None,
'ssl': False,
}
parsed_url = urllib.parse.urlparse(dsn)
if parsed_url.scheme.lower() not in ['imap', 'imaps']:
raise ValueError('Scheme must be "imap" or "imaps"')
account['ssl'] = parsed_url.scheme.lower() == 'imaps'
if parsed_url.hostname:
account['host'] = parsed_url.hostname
if parsed_url.port:
account['port'] = parsed_url.port
if parsed_url.username:
account['username'] = urllib.parse.unquote(parsed_url.username)
if parsed_url.password:
account['password'] = urllib.parse.unquote(parsed_url.password)
# prefill account name, if none was provided (by config.cfg) in case of calling it from commandline. can be overwritten by the query param 'name'
account['name'] = create_account_name(account, name)
if name:
account['name'] = name
else:
if account['username']:
account['name'] = account['username']
if account['host']:
account['name'] += '@' + account['host']
if parsed_url.path != '':
account['remote_folder'] = parsed_url.path.lstrip('/').rstrip('/')
if parsed_url.query != '':
query_params = urllib.parse.parse_qs(parsed_url.query)
# merge query params into account
for key, value in query_params.items():
if key == 'remote_folder':
if account['remote_folder'] is not None:
account['remote_folder'] += ',' + value[0]
else:
account['remote_folder'] = value[0]
elif key == 'ssl':
account['ssl'] = value[0].lower() == 'true'
# merge all others params, to be able to overwrite username, password, ... and future account options
else:
account[key] = value[0] if len(value) == 1 else value
return account
def create_account_name(account, name = None):
"""
If name is given, return it, otherwise return a name made up
of the username and host parts of the account.
"""
if name:
return name
else:
name = ''
if account['username']:
name = account['username']
if account['host']:
name += '@' + account['host']
return name
def account_to_dsn(account):
"""
Generate a DSN string from an account
"""
return 'imap{}://{}:{}@{}:{}/{}'.format('s' if account['ssl'] else '', urllib.parse.quote(account['username']), urllib.parse.quote(account['password']), account['host'], account['port'], urllib.parse.quote(account['remote_folder']))
def input_dsn(options):
"""
Asks the user to input the account details and print the full DSN.
If test_only is True, it will test the connection and optionally print the found folders.
"""
try:
account = {
'host': input('Host: ').strip(),
'port': int(input('Port [993]: ').strip() or '993'),
'ssl': input('Use SSL? [Y/n]: ').lower() != 'n',
'username': input('Username: ').strip(),
'password': input('Password: ').strip(),
'remote_folder': input('Remote folder (use __ALL__ to fetch all) [INBOX]: ').strip() or 'INBOX',
}
account['name'] = create_account_name(account)
options['accounts'] = [account]
print('\nDSN:\n {}'.format(account_to_dsn(account)))
except Exception as e:
errorHandler(e, 'Input DSN Error')
return options