Merge lp:~rajeevs1992/mailman.client/mailmancli into lp:mailman.client

Proposed by Rajeev S
Status: Needs review
Proposed branch: lp:~rajeevs1992/mailman.client/mailmancli
Merge into: lp:mailman.client
Diff against target: 4955 lines (+4719/-1)
39 files modified
README.rst (+29/-1)
conf.py (+330/-0)
setup.py (+4/-0)
src/mailmanclient.egg-info/PKG-INFO (+14/-0)
src/mailmanclient.egg-info/SOURCES.txt (+59/-0)
src/mailmanclient.egg-info/dependency_links.txt (+1/-0)
src/mailmanclient.egg-info/requires.txt (+5/-0)
src/mailmanclient.egg-info/top_level.txt (+1/-0)
src/mailmanclient/cli/client/cmdparser.py (+365/-0)
src/mailmanclient/cli/client/parsers/README.txt (+190/-0)
src/mailmanclient/cli/client/parsers/_set.py (+64/-0)
src/mailmanclient/cli/client/parsers/base.py (+62/-0)
src/mailmanclient/cli/client/parsers/create.py (+86/-0)
src/mailmanclient/cli/client/parsers/delete.py (+101/-0)
src/mailmanclient/cli/client/parsers/disable.py (+63/-0)
src/mailmanclient/cli/client/parsers/enable.py (+63/-0)
src/mailmanclient/cli/client/parsers/show.py (+101/-0)
src/mailmanclient/cli/client/parsers/show_var.py (+63/-0)
src/mailmanclient/cli/client/parsers/subscribe.py (+84/-0)
src/mailmanclient/cli/client/parsers/unset.py (+63/-0)
src/mailmanclient/cli/client/parsers/unsubscribe.py (+83/-0)
src/mailmanclient/cli/client/parsers/update.py (+114/-0)
src/mailmanclient/cli/client/shell.py (+394/-0)
src/mailmanclient/cli/core/domains.py (+136/-0)
src/mailmanclient/cli/core/lists.py (+227/-0)
src/mailmanclient/cli/core/misc.py (+69/-0)
src/mailmanclient/cli/core/preferences.py (+96/-0)
src/mailmanclient/cli/core/users.py (+189/-0)
src/mailmanclient/cli/docs/using_cli_shell.txt (+150/-0)
src/mailmanclient/cli/docs/using_cli_tools.txt (+311/-0)
src/mailmanclient/cli/docs/writing_a_new_command.txt (+57/-0)
src/mailmanclient/cli/lib/colors.py (+32/-0)
src/mailmanclient/cli/lib/mailman_utils.py (+117/-0)
src/mailmanclient/cli/lib/utils.py (+216/-0)
src/mailmanclient/cli/mmclient (+46/-0)
src/mailmanclient/cli/tests/test_domain.py (+223/-0)
src/mailmanclient/cli/tests/test_list.py (+285/-0)
src/mailmanclient/cli/tests/test_preference.py (+99/-0)
src/mailmanclient/cli/tests/test_user.py (+127/-0)
To merge this branch: bzr merge lp:~rajeevs1992/mailman.client/mailmancli
Reviewer Review Type Date Requested Status
Mailman Coders Pending
Review via email: mp+236052@code.launchpad.net

Commit message

Adds Command line tools and command line shell for mailman.client

Description of the change

GSoC project "Mailman CLI"

The branch contains the Mailman CLI shell as well as the command line tools built as a part of the GSoC 2014, Under the mentors Stephen J Turnbull, Abhilash Raj and Barry Warsaw.

To post a comment you must log in.

Unmerged revisions

75. By Rajeev S

- fixed bug in update global preference, which is readonly
- fixed/improved a few test cases

Global preferences are readonly, which is mentioned in the mmclient docs.
Option to edit the global preferences, which were previously allowed in
CLI are removed.

74. By Rajeev S

merged with base

73. By Rajeev S

Ported CLI to Py3

72. By Rajeev S

- made the docs sphinx compatiable
- added more docs

71. By Rajeev S

- added more tests, for the preferences methods
- added a check for HTTPErrors to make sure that
  correct errors are displayed
- scrubbed more code to enhance readability
- added readme for parsers

70. By Rajeev S

- modifed the setup.py to use scripts instead of entry_points
- added acks to Google and Mentors in file headers
- improved code for connection verification
- improved the `get_listing` function; moved to utils
- added colors as a seperate file, as per Steve's suggestion

69. By Rajeev S

- adds backup and restore tool
- fixed up installation routines
- refactored code to make it installable
- modified `in list` filter to support regular
  expressions
- updated docs and existing tests

68. By Rajeev S

- added expoort to csv function for domains, users
  and lists
- updated shell to support object names with whitespaces

67. By Rajeev S

- switched command parsing to YACC and LEX
  using python PLY module

66. By Rajeev S

- added update preference command
- introduced regex based validation for all commands
- regex stored in config.ini
- added docstrings aka help for all commands
- help stings printed upon error

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.rst'
--- README.rst 2015-01-01 19:34:58 +0000
+++ README.rst 2015-03-17 19:08:34 +0000
@@ -1,4 +1,9 @@
1Python bindings for the Mailman 3 REST API.1===========================================================
2mailman.client - Python bindings for the Mailman 3 REST API
3===========================================================
4
5This package is called ``mailman.client``.
6
27
3..8..
4 This file is part of mailman.client.9 This file is part of mailman.client.
@@ -48,6 +53,15 @@
48See the Launchpad project page for access to the Bazaar branch, bug report,53See the Launchpad project page for access to the Bazaar branch, bug report,
49etc.54etc.
5055
56Documentation
57=============
58
59A `simple guide`_ to using the library is available within this package, in
60the form of doctests. The manual is also available online in the Cheeseshop
61at:
62
63 http://package.python.org/mailman.client
64
5165
52Acknowledgements66Acknowledgements
53================67================
@@ -57,3 +71,17 @@
5771
58.. _`Cheese Shop`: http://pypi.python.org/mailman.client72.. _`Cheese Shop`: http://pypi.python.org/mailman.client
59.. _Launchpad: https://launchpad.net/mailman.client73.. _Launchpad: https://launchpad.net/mailman.client
74
75Table of Contents
76=================
77
78.. toctree::
79
80 src/mailmanclient/docs/using.txt
81 src/mailmanclient/cli/docs/using_cli_shell.txt
82 src/mailmanclient/cli/docs/using_cli_tools.txt
83 src/mailmanclient/cli/docs/writing_a_new_command.txt
84 src/mailmanclient/cli/client/parsers/README.txt
85 src/mailmanclient/NEWS.txt
86
87.. _`simple guide`: docs/using.html
6088
=== added file 'conf.py'
--- conf.py 1970-01-01 00:00:00 +0000
+++ conf.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,330 @@
1# -*- coding: utf-8 -*-
2#
3# Mailman CLI documentation build configuration file, created by
4# sphinx-quickstart on Sun Aug 17 13:11:12 2014.
5#
6# This file is execfile()d with the current directory set to its
7# containing dir.
8#
9# Note that not all possible configuration values are present in this
10# autogenerated file.
11#
12# All configuration values have a default; values that are commented out
13# serve to show the default.
14
15import sys
16import os
17
18# If extensions (or modules to document with autodoc) are in another directory,
19# add these directories to sys.path here. If the directory is relative to the
20# documentation root, use os.path.abspath to make it absolute, like shown here.
21#sys.path.insert(0, os.path.abspath('.'))
22
23# -- General configuration ------------------------------------------------
24
25# If your documentation needs a minimal Sphinx version, state it here.
26#needs_sphinx = '1.0'
27
28# Add any Sphinx extension module names here, as strings. They can be
29# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30# ones.
31extensions = [
32 'sphinx.ext.autodoc',
33]
34
35# Add any paths that contain templates here, relative to this directory.
36templates_path = ['_templates']
37
38# The suffix of source filenames.
39source_suffix = '.txt'
40
41# The encoding of source files.
42#source_encoding = 'utf-8-sig'
43
44# The master toctree document.
45master_doc = 'README'
46
47# General information about the project.
48project = u'Mailman CLI'
49copyright = u'2014, Rajeev S'
50
51# The version info for the project you're documenting, acts as replacement for
52# |version| and |release|, also used in various other places throughout the
53# built documents.
54#
55# The short X.Y version.
56version = '1'
57# The full version, including alpha/beta/rc tags.
58release = '1'
59
60# The language for content autogenerated by Sphinx. Refer to documentation
61# for a list of supported languages.
62#language = None
63
64# There are two options for replacing |today|: either, you set today to some
65# non-false value, then it is used:
66#today = ''
67# Else, today_fmt is used as the format for a strftime call.
68#today_fmt = '%B %d, %Y'
69
70# List of patterns, relative to source directory, that match files and
71# directories to ignore when looking for source files.
72exclude_patterns = ['_build']
73
74# The reST default role (used for this markup: `text`) to use for all
75# documents.
76#default_role = None
77
78# If true, '()' will be appended to :func: etc. cross-reference text.
79#add_function_parentheses = True
80
81# If true, the current module name will be prepended to all description
82# unit titles (such as .. function::).
83#add_module_names = True
84
85# If true, sectionauthor and moduleauthor directives will be shown in the
86# output. They are ignored by default.
87#show_authors = False
88
89# The name of the Pygments (syntax highlighting) style to use.
90pygments_style = 'sphinx'
91
92# A list of ignored prefixes for module index sorting.
93#modindex_common_prefix = []
94
95# If true, keep warnings as "system message" paragraphs in the built documents.
96#keep_warnings = False
97
98
99# -- Options for HTML output ----------------------------------------------
100
101# The theme to use for HTML and HTML Help pages. See the documentation for
102# a list of builtin themes.
103html_theme = 'default'
104
105# Theme options are theme-specific and customize the look and feel of a theme
106# further. For a list of options available for each theme, see the
107# documentation.
108#html_theme_options = {}
109
110# Add any paths that contain custom themes here, relative to this directory.
111#html_theme_path = []
112
113# The name for this set of Sphinx documents. If None, it defaults to
114# "<project> v<release> documentation".
115#html_title = None
116
117# A shorter title for the navigation bar. Default is the same as html_title.
118#html_short_title = None
119
120# The name of an image file (relative to this directory) to place at the top
121# of the sidebar.
122#html_logo = None
123
124# The name of an image file (within the static path) to use as favicon of the
125# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
126# pixels large.
127#html_favicon = None
128
129# Add any paths that contain custom static files (such as style sheets) here,
130# relative to this directory. They are copied after the builtin static files,
131# so a file named "default.css" will overwrite the builtin "default.css".
132html_static_path = ['_static']
133
134# Add any extra paths that contain custom files (such as robots.txt or
135# .htaccess) here, relative to this directory. These files are copied
136# directly to the root of the documentation.
137#html_extra_path = []
138
139# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
140# using the given strftime format.
141#html_last_updated_fmt = '%b %d, %Y'
142
143# If true, SmartyPants will be used to convert quotes and dashes to
144# typographically correct entities.
145#html_use_smartypants = True
146
147# Custom sidebar templates, maps document names to template names.
148#html_sidebars = {}
149
150# Additional templates that should be rendered to pages, maps page names to
151# template names.
152#html_additional_pages = {}
153
154# If false, no module index is generated.
155#html_domain_indices = True
156
157# If false, no index is generated.
158#html_use_index = True
159
160# If true, the index is split into individual pages for each letter.
161#html_split_index = False
162
163# If true, links to the reST sources are added to the pages.
164#html_show_sourcelink = True
165
166# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
167#html_show_sphinx = True
168
169# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
170#html_show_copyright = True
171
172# If true, an OpenSearch description file will be output, and all pages will
173# contain a <link> tag referring to it. The value of this option must be the
174# base URL from which the finished HTML is served.
175#html_use_opensearch = ''
176
177# This is the file name suffix for HTML files (e.g. ".xhtml").
178#html_file_suffix = None
179
180# Output file base name for HTML help builder.
181htmlhelp_basename = 'MailmanCLIdoc'
182
183
184# -- Options for LaTeX output ---------------------------------------------
185
186latex_elements = {
187# The paper size ('letterpaper' or 'a4paper').
188#'papersize': 'letterpaper',
189
190# The font size ('10pt', '11pt' or '12pt').
191#'pointsize': '10pt',
192
193# Additional stuff for the LaTeX preamble.
194#'preamble': '',
195}
196
197# Grouping the document tree into LaTeX files. List of tuples
198# (source start file, target name, title,
199# author, documentclass [howto, manual, or own class]).
200latex_documents = [
201 ('README', 'MailmanCLI.tex', u'Mailman CLI Documentation',
202 u'Rajeev S', 'manual'),
203]
204
205# The name of an image file (relative to this directory) to place at the top of
206# the title page.
207#latex_logo = None
208
209# For "manual" documents, if this is true, then toplevel headings are parts,
210# not chapters.
211#latex_use_parts = False
212
213# If true, show page references after internal links.
214#latex_show_pagerefs = False
215
216# If true, show URL addresses after external links.
217#latex_show_urls = False
218
219# Documents to append as an appendix to all manuals.
220#latex_appendices = []
221
222# If false, no module index is generated.
223#latex_domain_indices = True
224
225
226# -- Options for manual page output ---------------------------------------
227
228# One entry per manual page. List of tuples
229# (source start file, name, description, authors, manual section).
230man_pages = [
231 ('README', 'mailmancli', u'Mailman CLI Documentation',
232 [u'Rajeev S'], 1)
233]
234
235# If true, show URL addresses after external links.
236#man_show_urls = False
237
238
239# -- Options for Texinfo output -------------------------------------------
240
241# Grouping the document tree into Texinfo files. List of tuples
242# (source start file, target name, title, author,
243# dir menu entry, description, category)
244texinfo_documents = [
245 ('README', 'MailmanCLI', u'Mailman CLI Documentation',
246 u'Rajeev S', 'MailmanCLI', 'One line description of project.',
247 'Miscellaneous'),
248]
249
250# Documents to append as an appendix to all manuals.
251#texinfo_appendices = []
252
253# If false, no module index is generated.
254#texinfo_domain_indices = True
255
256# How to display URL addresses: 'footnote', 'no', or 'inline'.
257#texinfo_show_urls = 'footnote'
258
259# If true, do not generate a @detailmenu in the "Top" node's menu.
260#texinfo_no_detailmenu = False
261
262
263# -- Options for Epub output ----------------------------------------------
264
265# Bibliographic Dublin Core info.
266epub_title = u'Mailman CLI'
267epub_author = u'Rajeev S'
268epub_publisher = u'Rajeev S'
269epub_copyright = u'2014, Rajeev S'
270
271# The basename for the epub file. It defaults to the project name.
272#epub_basename = u'Mailman CLI'
273
274# The HTML theme for the epub output. Since the default themes are not optimized
275# for small screen space, using the same theme for HTML and epub output is
276# usually not wise. This defaults to 'epub', a theme designed to save visual
277# space.
278#epub_theme = 'epub'
279
280# The language of the text. It defaults to the language option
281# or en if the language is not set.
282#epub_language = ''
283
284# The scheme of the identifier. Typical schemes are ISBN or URL.
285#epub_scheme = ''
286
287# The unique identifier of the text. This can be a ISBN number
288# or the project homepage.
289#epub_identifier = ''
290
291# A unique identification for the text.
292#epub_uid = ''
293
294# A tuple containing the cover image and cover page html template filenames.
295#epub_cover = ()
296
297# A sequence of (type, uri, title) tuples for the guide element of content.opf.
298#epub_guide = ()
299
300# HTML files that should be inserted before the pages created by sphinx.
301# The format is a list of tuples containing the path and title.
302#epub_pre_files = []
303
304# HTML files shat should be inserted after the pages created by sphinx.
305# The format is a list of tuples containing the path and title.
306#epub_post_files = []
307
308# A list of files that should not be packed into the epub file.
309epub_exclude_files = ['search.html']
310
311# The depth of the table of contents in toc.ncx.
312#epub_tocdepth = 3
313
314# Allow duplicate toc entries.
315#epub_tocdup = True
316
317# Choose between 'default' and 'includehidden'.
318#epub_tocscope = 'default'
319
320# Fix unsupported image types using the PIL.
321#epub_fix_images = False
322
323# Scale large images.
324#epub_max_image_width = 0
325
326# How to display URL addresses: 'footnote', 'no', or 'inline'.
327#epub_show_urls = 'inline'
328
329# If false, no index is generated.
330#epub_use_index = True
0331
=== modified file 'setup.py'
--- setup.py 2015-01-01 21:26:10 +0000
+++ setup.py 2015-03-17 19:08:34 +0000
@@ -30,6 +30,7 @@
30 packages=find_packages('src'),30 packages=find_packages('src'),
31 package_dir = {'': 'src'},31 package_dir = {'': 'src'},
32 include_package_data=True,32 include_package_data=True,
33 scripts=['src/mailmanclient/cli/mmclient'],
33 maintainer='Barry Warsaw',34 maintainer='Barry Warsaw',
34 maintainer_email='barry@list.org',35 maintainer_email='barry@list.org',
35 description=description('README.rst'),36 description=description('README.rst'),
@@ -42,5 +43,8 @@
42 install_requires=[43 install_requires=[
43 'httplib2',44 'httplib2',
44 'six',45 'six',
46 'mock',
47 'tabulate',
48 'ply',
45 ],49 ],
46 )50 )
4751
=== added directory 'src/mailmanclient.egg-info'
=== added file 'src/mailmanclient.egg-info/PKG-INFO'
--- src/mailmanclient.egg-info/PKG-INFO 1970-01-01 00:00:00 +0000
+++ src/mailmanclient.egg-info/PKG-INFO 2015-03-17 19:08:34 +0000
@@ -0,0 +1,14 @@
1Metadata-Version: 1.1
2Name: mailmanclient
3Version: 1.0.0
4Summary: Python bindings for the Mailman 3 REST API.
5Home-page: http://launchpad.net/mailman.client
6Author: Barry Warsaw
7Author-email: barry@list.org
8License: LGPLv3
9Download-URL: https://launchpad.net/mailman.client/+download
10Description: src/mailmanclient/README.rst
11
12 src/mailmanclient/NEWS.rst
13
14Platform: UNKNOWN
015
=== added file 'src/mailmanclient.egg-info/SOURCES.txt'
--- src/mailmanclient.egg-info/SOURCES.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient.egg-info/SOURCES.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,59 @@
1MANIFEST.in
2Makefile
3README.rst
4README.txt
5conf.py
6distribute_setup.py
7setup.cfg
8setup.py
9setup_helpers.py
10template.py
11src/mailmanclient/NEWS.txt
12src/mailmanclient/__init__.py
13src/mailmanclient/_client.py
14src/mailmanclient.egg-info/PKG-INFO
15src/mailmanclient.egg-info/SOURCES.txt
16src/mailmanclient.egg-info/dependency_links.txt
17src/mailmanclient.egg-info/requires.txt
18src/mailmanclient.egg-info/top_level.txt
19src/mailmanclient/cli/__init__.py
20src/mailmanclient/cli/mmclient
21src/mailmanclient/cli/client/__init__.py
22src/mailmanclient/cli/client/cmdparser.py
23src/mailmanclient/cli/client/shell.py
24src/mailmanclient/cli/client/parsers/README.txt
25src/mailmanclient/cli/client/parsers/__init__.py
26src/mailmanclient/cli/client/parsers/_set.py
27src/mailmanclient/cli/client/parsers/base.py
28src/mailmanclient/cli/client/parsers/create.py
29src/mailmanclient/cli/client/parsers/delete.py
30src/mailmanclient/cli/client/parsers/disable.py
31src/mailmanclient/cli/client/parsers/enable.py
32src/mailmanclient/cli/client/parsers/show.py
33src/mailmanclient/cli/client/parsers/show_var.py
34src/mailmanclient/cli/client/parsers/subscribe.py
35src/mailmanclient/cli/client/parsers/unset.py
36src/mailmanclient/cli/client/parsers/unsubscribe.py
37src/mailmanclient/cli/client/parsers/update.py
38src/mailmanclient/cli/core/__init__.py
39src/mailmanclient/cli/core/domains.py
40src/mailmanclient/cli/core/lists.py
41src/mailmanclient/cli/core/misc.py
42src/mailmanclient/cli/core/preferences.py
43src/mailmanclient/cli/core/users.py
44src/mailmanclient/cli/docs/using_cli_shell.txt
45src/mailmanclient/cli/docs/using_cli_tools.txt
46src/mailmanclient/cli/docs/writing_a_new_command.txt
47src/mailmanclient/cli/lib/__init__.py
48src/mailmanclient/cli/lib/colors.py
49src/mailmanclient/cli/lib/mailman_utils.py
50src/mailmanclient/cli/lib/utils.py
51src/mailmanclient/cli/tests/__init__.py
52src/mailmanclient/cli/tests/test_domain.py
53src/mailmanclient/cli/tests/test_list.py
54src/mailmanclient/cli/tests/test_preference.py
55src/mailmanclient/cli/tests/test_user.py
56src/mailmanclient/docs/__init__.py
57src/mailmanclient/docs/using.txt
58src/mailmanclient/tests/__init__.py
59src/mailmanclient/tests/test_docs.py
0\ No newline at end of file60\ No newline at end of file
161
=== added file 'src/mailmanclient.egg-info/dependency_links.txt'
--- src/mailmanclient.egg-info/dependency_links.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient.egg-info/dependency_links.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,1 @@
1
02
=== added file 'src/mailmanclient.egg-info/requires.txt'
--- src/mailmanclient.egg-info/requires.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient.egg-info/requires.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,5 @@
1httplib2
2six
3mock
4tabulate
5ply
06
=== added file 'src/mailmanclient.egg-info/top_level.txt'
--- src/mailmanclient.egg-info/top_level.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient.egg-info/top_level.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,1 @@
1mailmanclient
02
=== added directory 'src/mailmanclient/cli'
=== added file 'src/mailmanclient/cli/__init__.py'
=== added directory 'src/mailmanclient/cli/client'
=== added file 'src/mailmanclient/cli/client/__init__.py'
=== added file 'src/mailmanclient/cli/client/cmdparser.py'
--- src/mailmanclient/cli/client/cmdparser.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/cmdparser.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,365 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24from argparse import ArgumentParser
25from mailmanclient.cli.core.misc import Misc
26from mailmanclient.cli.core.users import Users
27from mailmanclient.cli.core.lists import Lists
28from mailmanclient.cli.core.domains import Domains
29from mailmanclient.cli.core.preferences import Preferences
30from mailmanclient.cli.lib.mailman_utils import MailmanUtils
31
32utils = MailmanUtils()
33
34
35class CmdParser():
36 def __init__(self, command):
37 parser = ArgumentParser(description='Mailman Command Tools')
38 self.initialize_options(parser)
39 self.arguments = vars(parser.parse_args())
40
41 def initialize_options(self, parser):
42 action = parser.add_subparsers(dest='action')
43
44 # Parser for the action `show`
45 action_show = action.add_parser('show')
46 scope = action_show.add_subparsers(dest='scope')
47
48 # Show lists
49 show_list = scope.add_parser('list')
50 show_list.add_argument('list',
51 help='List details about LIST',
52 nargs='?')
53 show_list.add_argument('-d',
54 '--domain',
55 help='Filter by DOMAIN')
56 show_list.add_argument('-v',
57 '--verbose',
58 help='Detailed listing',
59 action='store_true')
60 show_list.add_argument('--no-header',
61 help='Omit headings in detailed listing',
62 action='store_true')
63 show_list.add_argument('--csv',
64 help='Output as CSV, Specify filename')
65
66 # Show domains
67 show_domain = scope.add_parser('domain')
68 show_domain.add_argument('domain',
69 help='List details about DOMAIN',
70 nargs='?')
71 show_domain.add_argument('-v',
72 '--verbose',
73 help='Detailed listing',
74 action='store_true')
75 show_domain.add_argument('--no-header',
76 help='Omit headings in detailed listing',
77 action='store_true')
78 show_domain.add_argument('--csv',
79 help='Output as CSV, Specify filename')
80
81 # Show users
82 show_user = scope.add_parser('user')
83 show_user.add_argument('user',
84 help='List details about USER',
85 nargs='?')
86 show_user.add_argument('-v',
87 '--verbose',
88 help='Detailed listing',
89 action='store_true')
90 show_user.add_argument('--no-header',
91 help='Omit headings in detailed listing',
92 action='store_true')
93 show_user.add_argument('-l',
94 '--list',
95 help='Specify list name',
96 dest='list_name')
97 show_user.add_argument('--csv',
98 help='Output as CSV, Specify filename')
99
100 # Show preferences
101 preferences = ['receive_list_copy', 'hide_address',
102 'preferred_language', 'acknowledge_posts',
103 'delivery_mode', 'receive_own_postings',
104 'http_etag', 'self_link', 'delivery_status']
105 show_preference = scope.add_parser('preference')
106 show_scope = show_preference.add_subparsers(dest='show_scope')
107
108 show_scope.add_parser('global')
109
110 user_show = show_scope.add_parser('user')
111 user_show.add_argument('--email',
112 help='Email of user whose '
113 'preference is to be shown',
114 required=True)
115
116 address_show = show_scope.add_parser('address')
117 address_show.add_argument('--email',
118 help='Address whose preference'
119 ' is to be shown',
120 required=True)
121
122 member_show = show_scope.add_parser('member')
123 member_show.add_argument('--email',
124 help='Address whose preference'
125 ' is to be shown',
126 required=True)
127 member_show.add_argument('--list',
128 help='FQDN name of list',
129 required=True)
130
131 show_preference.add_argument('key',
132 help='Specify setting name',
133 choices=preferences)
134
135 # Parser for the action `create`
136 action_create = action.add_parser('create')
137 scope = action_create.add_subparsers(dest='scope')
138
139 # Create list
140 create_list = scope.add_parser('list')
141 create_list.add_argument('list',
142 help='List name. e.g., list@domain.org')
143 # Create domain
144 create_domain = scope.add_parser('domain')
145 create_domain.add_argument('domain',
146 help='Create domain DOMAIN')
147 create_domain.add_argument('--contact',
148 help='Contact address for domain')
149 # Create users
150 create_user = scope.add_parser('user')
151 create_user.add_argument('email',
152 help='Create user foo@bar.com')
153 create_user.add_argument('--password',
154 help='User password',
155 required=True)
156 create_user.add_argument('--name',
157 help='Display name of the user',
158 required=True)
159
160 # Parser for the action `delete`
161 action_delete = action.add_parser('delete')
162 scope = action_delete.add_subparsers(dest='scope')
163
164 # Delete list
165 delete_list = scope.add_parser('list')
166 delete_list.add_argument('list',
167 help='List name. e.g., list@domain.org')
168 delete_list.add_argument('--yes',
169 help='Force delete',
170 action='store_true')
171 # Delete domain
172 delete_domain = scope.add_parser('domain')
173 delete_domain.add_argument('domain',
174 help='Domain name. e.g., domain.org')
175 delete_domain.add_argument('--yes',
176 help='Force delete',
177 action='store_true')
178 # Delete user
179 delete_user = scope.add_parser('user')
180 delete_user.add_argument('user',
181 help='User email e.g., foo@bar.com')
182 delete_user.add_argument('--yes',
183 help='Force delete',
184 action='store_true')
185 # Show Members of a list
186 action_show_member = action.add_parser('show-members')
187 scope = action_show_member.add_subparsers(dest='scope')
188 show_member = scope.add_parser('list')
189 show_member.add_argument('list',
190 help='Show members of LIST')
191 show_member.add_argument('-v',
192 '--verbose',
193 help='Detailed listing',
194 action='store_true')
195 show_member.add_argument('--no-header',
196 help='Omit headings in detailed listing',
197 action='store_true')
198
199 # Parser for the action `subscribe`
200 action_subscribe = action.add_parser('subscribe')
201 scope = action_subscribe.add_subparsers(dest='scope')
202 subscribe_user = scope.add_parser('user')
203 subscribe_user.add_argument('users',
204 help='User email list',
205 nargs='+')
206 subscribe_user.add_argument('-l',
207 '--list',
208 help='Specify list name',
209 dest='list_name',
210 required=True)
211 subscribe_user.add_argument('--quiet',
212 help='Do not display feedback',
213 action='store_true')
214
215 # Parser for the action `unsubscribe`
216 action_subscribe = action.add_parser('unsubscribe')
217 scope = action_subscribe.add_subparsers(dest='scope')
218 subscribe_user = scope.add_parser('user')
219 subscribe_user.add_argument('users',
220 help='User email list',
221 nargs='+')
222 subscribe_user.add_argument('-l',
223 '--list',
224 help='Specify list name',
225 dest='list_name',
226 required=True)
227 subscribe_user.add_argument('--quiet',
228 help='Do not display feedback',
229 action='store_true')
230 # Moderation Tools
231
232 # Add Moderator
233 action_add_moderator = action.add_parser('add-moderator')
234 scope = action_add_moderator.add_subparsers(dest='scope')
235 add_moderator = scope.add_parser('list')
236 add_moderator.add_argument('list',
237 help='Specify list name')
238 add_moderator.add_argument('-u',
239 '--user',
240 help='User email list',
241 dest='users',
242 nargs='+',
243 required=True)
244 add_moderator.add_argument('--quiet',
245 help='Do not display feedback',
246 action='store_true')
247 # Add Owner
248 action_add_owner = action.add_parser('add-owner')
249 scope = action_add_owner.add_subparsers(dest='scope')
250 add_owner = scope.add_parser('list')
251 add_owner.add_argument('list',
252 help='Specify list name')
253 add_owner.add_argument('-u',
254 '--user',
255 help='User email list',
256 dest='users',
257 nargs='+')
258 add_owner.add_argument('--quiet',
259 help='Do not display feedback',
260 action='store_true')
261 # Remove Moderator
262 action_remove_moderator = action.add_parser('remove-moderator')
263 scope = action_remove_moderator.add_subparsers(dest='scope')
264 remove_moderator = scope.add_parser('list')
265 remove_moderator.add_argument('list',
266 help='Specify list name')
267 remove_moderator.add_argument('-u',
268 '--user',
269 dest='users',
270 help='User email list',
271 nargs='+')
272 remove_moderator.add_argument('--quiet',
273 help='Do not display feedback',
274 action='store_true')
275 # Remove Owner
276 action_remove_owner = action.add_parser('remove-owner')
277 scope = action_remove_owner.add_subparsers(dest='scope')
278 remove_owner = scope.add_parser('list')
279 remove_owner.add_argument('list',
280 help='Specify list name')
281 remove_owner.add_argument('-u',
282 '--user',
283 help='User email list',
284 dest='users',
285 nargs='+')
286 remove_owner.add_argument('--quiet',
287 help='Do not display feedback',
288 action='store_true')
289 # Edit preferences
290 action_update = action.add_parser('update')
291 scope = action_update.add_subparsers(dest='scope')
292 update_preference = scope.add_parser('preference')
293 update_scope = update_preference.add_subparsers(dest='update_scope')
294
295 user_update = update_scope.add_parser('user')
296 user_update.add_argument('--email',
297 help='Email of user whose '
298 'preference is to be changed',
299 required=True)
300
301 address_update = update_scope.add_parser('address')
302 address_update.add_argument('--email',
303 help='Address whose preference'
304 ' is to be changed',
305 required=True)
306
307 member_update = update_scope.add_parser('member')
308 member_update.add_argument('--email',
309 help='Address whose preference'
310 ' is to be changed',
311 required=True)
312 member_update.add_argument('--list',
313 help='FQDN name of list',
314 required=True)
315
316 update_preference.add_argument('key',
317 help='Specify setting name',
318 choices=preferences)
319 update_preference.add_argument('value',
320 help='Specify setting value')
321 # Backup Tool
322 action_backup = action.add_parser('backup')
323 action_backup.add_argument('output',
324 help='Specify file to write backup')
325
326 # Restore Tool
327 action_backup = action.add_parser('restore')
328 action_backup.add_argument('backup',
329 help='Specify backup file location')
330 # Global options
331 parser.add_argument('--host', help='REST API host address')
332 parser.add_argument('--port', help='REST API host port')
333 parser.add_argument('--restuser', help='REST API username')
334 parser.add_argument('--restpass', help='REST API password')
335
336 def run(self):
337 host = self.arguments['host']
338 port = self.arguments['port']
339 username = self.arguments['restuser']
340 password = self.arguments['restpass']
341 client = utils.connect(host=host, port=port,
342 username=username, password=password)
343 if 'scope' not in self.arguments:
344 self.run_misc_actions()
345 return
346 scopes = {}
347 scopes['user'] = Users
348 scopes['list'] = Lists
349 scopes['domain'] = Domains
350 scopes['preference'] = Preferences
351 self.arguments['action'] = self.arguments['action'].replace('-', '_')
352 try:
353 scope_object = scopes[self.arguments['scope']](client)
354 action_name = self.arguments['action']
355 action = getattr(scope_object, action_name)
356 action(self.arguments)
357 except Exception as e:
358 utils.error(e)
359 exit(1)
360
361 def run_misc_actions(self):
362 action_name = self.arguments['action']
363 misc = Misc()
364 action = getattr(misc, action_name)
365 action(self.arguments)
0366
=== added directory 'src/mailmanclient/cli/client/parsers'
=== added file 'src/mailmanclient/cli/client/parsers/README.txt'
--- src/mailmanclient/cli/client/parsers/README.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/README.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,190 @@
1Mailman Shell Parsers
2**********************
3
4This directory consists of the various parsers that
5have been built for use with the mailman shell. The shell
6parser has been built in such a way that each command has
7a different and independent parser, so that adding new commands
8can be done flexibly and easily, without worrying about the
9existing code.
10
11The `show` command
12=================
13
14The command to display the mailman objects
15
16Tokens
17------
18::
19
20 SHOW : 'show'
21 SCOPE : '(user|domain|list)s?'
22 WHERE : 'where'
23 OP : '=|in|like'
24 AND : 'and'
25 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
26
27Grammar
28-------
29::
30
31 S : SHOW SCOPE FILTER ;
32 FILTER : WHERE EXP | ;
33 EXP : STRING OP STRING CONJ ;
34 CONJ : AND EXP | ;
35
36The `create` command
37====================
38
39The commands to create mailman objects
40
41Tokens
42------
43::
44
45 CREATE : 'create'
46 SCOPE : 'user|domain|list'
47 WITH : 'with'
48 AND : 'and'
49 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
50
51Grammar
52-------
53::
54
55 S : CREATE SCOPE WITH EXP ;
56 EXP : STRING "=" STRING CONJ ;
57 CONJ : AND EXP | ;
58
59The `delete` command
60====================
61
62The commands to delete mailman objects
63
64Tokens
65------
66::
67
68 DELETE : 'delete'
69 SCOPE : '(user|domain|list)s?'
70 WHERE : 'where'
71 OP : '=|in|like'
72 AND : 'and'
73 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
74
75Grammar
76-------
77::
78
79 S : DELETE SCOPE FILTER ;
80 FILTER : WHERE EXP | ;
81 EXP : STRING OP STRING CONJ ;
82 CONJ : AND EXP | ;
83
84The `subscribe` Command
85=====================
86
87The commands to subscribe a list of users to a mailing lists
88
89Tokens
90------
91::
92
93 SUBSCRIBE : 'subscribe'
94 USER : '(user)?'
95 TO : 'to'
96 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
97
98Grammar
99-------
100::
101
102 S : SUBSCRIBE USER USERLIST TO STRING ;
103 USERLIST : STRING NEXT ;
104 NEXT : USERLIST | ;
105
106The `unsubscribe` Command
107=========================
108
109The commands to unsubscribe a list of users from a mailing lists
110
111Tokens
112------
113::
114
115 UNSUBSCRIBE : 'unsubscribe'
116 USER : '(user)?'
117 FROM : 'from'
118 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
119
120Grammar
121-------
122::
123
124 S : UNSUBSCRIBE USER USERLIST TO STRING ;
125 USERLIST : STRING NEXT ;
126 NEXT : USERLIST | ;
127
128The `unset` command
129===================
130
131Command to unset a shell variable
132
133Tokens
134------
135::
136
137 UNSET : 'unset'
138 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
139
140Grammar
141-------
142::
143
144 S : UNSET STRING ;
145
146The `set` Command
147=================
148
149Command to set a shell variable
150
151Tokens
152------
153::
154
155 SET : 'set'
156 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
157
158Grammar
159-------
160::
161
162 S : SET STRING "=" STRING ;
163
164The `update` command
165====================
166
167Command to update a preference
168
169Tokens
170------
171::
172
173 UPDATE : 'update'
174 PREFERENCE : 'preference'
175 STRING : '`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
176 TO : 'to'
177 WITH : 'with'
178 AND : 'and'
179 FOR : 'for'
180 GLOBALLY : 'globally'
181 DOMAIN : 'user|address|member'
182
183Grammar
184-------
185::
186
187 S : UPDATE PREFERENCE STRING TO STRING E ;
188 E : GLOBALLY | FOR DOMAIN WITH EXP ;
189 EXP : STRING "=" STRING CONJ ;
190 CONJ : AND EXP | ;
0191
=== added file 'src/mailmanclient/cli/client/parsers/__init__.py'
=== added file 'src/mailmanclient/cli/client/parsers/_set.py'
--- src/mailmanclient/cli/client/parsers/_set.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/_set.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,64 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Set(Parser):
29 tokens = ('SET', 'STRING')
30 literals = ['=', '`']
31 t_ignore = " \t"
32
33 def t_SET(self, t):
34 r'set'
35 return t
36
37 def t_STRING(self, t):
38 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
39 t.value = t.value.replace('`', '')
40 return t
41
42 def t_newline(self, t):
43 r'\n+'
44 t.lexer.lineno += t.value.count("\n")
45 return
46
47 def t_error(self, t):
48 raise Exception("Illegal character '%s'" % t.value[0])
49 t.lexer.skip(1)
50
51 def p_statement_scope(self, p):
52 '''S : SET STRING "=" STRING'''
53 self.arguments['key'] = p[2]
54 self.arguments['value'] = p[4]
55
56 def p_error(self, p):
57 if p:
58 raise Exception("Syntax error at '%s'" % p.value)
59 else:
60 raise Exception("Syntax error : Incomplete Command")
61
62 def parse(self, shell):
63 yacc.parse(shell.line)
64 return self.arguments
065
=== added file 'src/mailmanclient/cli/client/parsers/base.py'
--- src/mailmanclient/cli/client/parsers/base.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/base.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,62 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import os
25import ply.lex as lex
26import ply.yacc as yacc
27
28
29class Parser:
30 """
31 Base class for a lexer/parser that has the rules defined as methods
32 """
33 tokens = ()
34 precedence = ()
35 arguments = {}
36
37 def __init__(self, **kw):
38 self.debug = kw.get('debug', 0)
39 self.names = {}
40 self.arguments = {}
41 try:
42 modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_"
43 modname += self.__class__.__name__
44 except:
45 modname = "parser"+"_"+self.__class__.__name__
46 self.debugfile = modname + ".dbg"
47 self.tabmodule = modname + "_" + "parsetab"
48
49 if not os.path.exists('/tmp/parser_files'):
50 os.mkdir('/tmp/parser_files')
51
52 lex.lex(module=self, debug=self.debug)
53 yacc.yacc(module=self,
54 debug=self.debug,
55 debugfile=self.debugfile,
56 tabmodule=self.tabmodule,
57 outputdir='/tmp/parser_files')
58
59 def stem(self, token):
60 if token.value[-1] == 's':
61 token.value = token.value[:-1]
62 return token
063
=== added file 'src/mailmanclient/cli/client/parsers/create.py'
--- src/mailmanclient/cli/client/parsers/create.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/create.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,86 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Create(Parser):
29 tokens = ('CREATE', 'SCOPE', 'STRING', 'WITH', 'AND')
30 literals = ['+', '`', '=']
31 t_ignore = " \t"
32
33 def t_CREATE(self, t):
34 r'create'
35 return t
36
37 def t_SCOPE(self, t):
38 'user|domain|list'
39 return t
40
41 def t_WITH(self, t):
42 'with'
43 return t
44
45 def t_AND(self, t):
46 'and'
47 return t
48
49 def t_STRING(self, t):
50 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
51 t.value = t.value.replace('`', '')
52 return t
53
54 def t_newline(self, t):
55 r'\n+'
56 t.lexer.lineno += t.value.count("\n")
57 return
58
59 def t_error(self, t):
60 raise Exception("Illegal character '%s'" % t.value[0])
61 t.lexer.skip(1)
62
63 def p_start_statement(self, p):
64 '''S : CREATE SCOPE WITH EXP'''
65 self.arguments['scope'] = p[2]
66
67 def p_exp_condition(self, p):
68 '''EXP : STRING "=" STRING CONJ'''
69 if 'properties' not in self.arguments:
70 self.arguments['properties'] = {}
71 self.arguments['properties'][p[1]] = p[3]
72
73 def p_conj_exp(self, p):
74 ''' CONJ : AND EXP
75 |'''
76 pass
77
78 def p_error(self, p):
79 if p:
80 raise Exception("Syntax error at '%s'" % p.value)
81 else:
82 raise Exception("Syntax error : Incomplete Command")
83
84 def parse(self, shell):
85 yacc.parse(shell.line)
86 return self.arguments
087
=== added file 'src/mailmanclient/cli/client/parsers/delete.py'
--- src/mailmanclient/cli/client/parsers/delete.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/delete.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,101 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26from mailmanclient.cli.lib.mailman_utils import MailmanUtils
27
28
29class Delete(Parser):
30 tokens = ('DELETE', 'SCOPE', 'STRING', 'WHERE', 'OP', 'AND')
31 literals = ['+', '`']
32 t_ignore = " \t"
33
34 def t_DELETE(self, t):
35 r'delete'
36 return t
37
38 def t_SCOPE(self, t):
39 '(user|domain|list)s?'
40 return self.stem(t)
41
42 def t_WHERE(self, t):
43 'where'
44 return t
45
46 def t_OP(self, t):
47 '=|in|like'
48 return t
49
50 def t_AND(self, t):
51 'and'
52 return t
53
54 def t_STRING(self, t):
55 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
56 t.value = t.value.replace('`', '')
57 return t
58
59 def t_newline(self, t):
60 r'\n+'
61 t.lexer.lineno += t.value.count("\n")
62 return
63
64 def t_error(self, t):
65 raise Exception("Illegal character '%s'" % t.value[0])
66 t.lexer.skip(1)
67
68 def p_statement_scope(self, p):
69 '''S : DELETE SCOPE FILTER'''
70 self.arguments['scope'] = p[2]
71
72 def p_filter(self, p):
73 '''FILTER : WHERE EXP
74 |'''
75 pass
76
77 def p_exp_condition(self, p):
78 '''EXP : STRING OP STRING CONJ'''
79 if 'filters' not in self.arguments:
80 self.arguments['filters'] = []
81 if p[2] == 'in':
82 self.arguments['filters'].append((p[3], p[2], p[1]))
83 else:
84 self.arguments['filters'].append((p[1], p[2], p[3]))
85
86 def p_conj_exp(self, p):
87 ''' CONJ : AND EXP
88 |'''
89 pass
90
91 def p_error(self, p):
92 if p:
93 raise Exception("Syntax error at '%s'" % p.value)
94 else:
95 raise Exception("Syntax error : Incomplete Command")
96
97 def parse(self, shell):
98 yacc.parse(shell.line)
99 utils = MailmanUtils()
100 self.arguments = utils.add_reserved_vars(self.arguments, shell)
101 return self.arguments
0102
=== added file 'src/mailmanclient/cli/client/parsers/disable.py'
--- src/mailmanclient/cli/client/parsers/disable.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/disable.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,63 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Disable(Parser):
29 tokens = ('DISABLE', 'WHAT')
30 literals = ['=', '`']
31 t_ignore = " \t"
32
33 def t_DISABLE(self, t):
34 r'disable'
35 return t
36
37 def t_WHAT(self, t):
38 r'env'
39 t.value = t.value.replace('`', '')
40 return t
41
42 def t_newline(self, t):
43 r'\n+'
44 t.lexer.lineno += t.value.count("\n")
45 return
46
47 def t_error(self, t):
48 raise Exception("Illegal character '%s'" % t.value[0])
49 t.lexer.skip(1)
50
51 def p_statment(self, p):
52 '''S : DISABLE WHAT'''
53 self.arguments['what'] = p[2]
54
55 def p_error(self, p):
56 if p:
57 raise Exception("Syntax error at '%s'" % p.value)
58 else:
59 raise Exception("Syntax error : Incomplete Command")
60
61 def parse(self, shell):
62 yacc.parse(shell.line)
63 return self.arguments
064
=== added file 'src/mailmanclient/cli/client/parsers/enable.py'
--- src/mailmanclient/cli/client/parsers/enable.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/enable.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,63 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Enable(Parser):
29 tokens = ('ENABLE', 'WHAT')
30 literals = ['=', '`']
31 t_ignore = " \t"
32
33 def t_ENABLE(self, t):
34 r'enable'
35 return t
36
37 def t_WHAT(self, t):
38 r'env'
39 t.value = t.value.replace('`', '')
40 return t
41
42 def t_newline(self, t):
43 r'\n+'
44 t.lexer.lineno += t.value.count("\n")
45 return
46
47 def t_error(self, t):
48 raise Exception("Illegal character '%s'" % t.value[0])
49 t.lexer.skip(1)
50
51 def p_statment(self, p):
52 '''S : ENABLE WHAT'''
53 self.arguments['what'] = p[2]
54
55 def p_error(self, p):
56 if p:
57 raise Exception("Syntax error at '%s'" % p.value)
58 else:
59 raise Exception("Syntax error : Incomplete Command")
60
61 def parse(self, shell):
62 yacc.parse(shell.line)
63 return self.arguments
064
=== added file 'src/mailmanclient/cli/client/parsers/show.py'
--- src/mailmanclient/cli/client/parsers/show.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/show.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,101 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26from mailmanclient.cli.lib.mailman_utils import MailmanUtils
27
28
29class Show(Parser):
30 tokens = ('SHOW', 'SCOPE', 'STRING', 'WHERE', 'OP', 'AND')
31 literals = ['+', '`']
32 t_ignore = " \t"
33
34 def t_SHOW(self, t):
35 r'show'
36 return t
37
38 def t_SCOPE(self, t):
39 '(user|domain|list)s?'
40 return self.stem(t)
41
42 def t_WHERE(self, t):
43 'where'
44 return t
45
46 def t_OP(self, t):
47 '=|in|like'
48 return t
49
50 def t_AND(self, t):
51 'and'
52 return t
53
54 def t_STRING(self, t):
55 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
56 t.value = t.value.replace('`', '')
57 return t
58
59 def t_newline(self, t):
60 r'\n+'
61 t.lexer.lineno += t.value.count("\n")
62 return
63
64 def t_error(self, t):
65 raise Exception("Illegal character '%s'" % t.value[0])
66 t.lexer.skip(1)
67
68 def p_statement_scope(self, p):
69 '''S : SHOW SCOPE FILTER'''
70 self.arguments['scope'] = p[2]
71
72 def p_filter(self, p):
73 '''FILTER : WHERE EXP
74 |'''
75 pass
76
77 def p_exp_condition(self, p):
78 '''EXP : STRING OP STRING CONJ'''
79 if 'filters' not in self.arguments:
80 self.arguments['filters'] = []
81 if p[2] == 'in':
82 self.arguments['filters'].append((p[3], p[2], p[1]))
83 else:
84 self.arguments['filters'].append((p[1], p[2], p[3]))
85
86 def p_conj_exp(self, p):
87 ''' CONJ : AND EXP
88 |'''
89 pass
90
91 def p_error(self, p):
92 if p:
93 raise Exception("Syntax error at '%s'" % p.value)
94 else:
95 raise Exception("Syntax error : Incomplete Command")
96
97 def parse(self, shell):
98 yacc.parse(shell.line)
99 utils = MailmanUtils()
100 self.arguments = utils.add_reserved_vars(self.arguments, shell)
101 return self.arguments
0102
=== added file 'src/mailmanclient/cli/client/parsers/show_var.py'
--- src/mailmanclient/cli/client/parsers/show_var.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/show_var.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,63 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class ShowVar(Parser):
29 tokens = ('SHOW', 'STRING')
30 literals = ['=', '`']
31 t_ignore = " \t"
32
33 def t_SHOW(self, t):
34 r'show_var'
35 return t
36
37 def t_STRING(self, t):
38 r'`([a-zA-Z0-9_@\.\*\-]*)`'
39 t.value = t.value.replace('`', '')
40 return t
41
42 def t_newline(self, t):
43 r'\n+'
44 t.lexer.lineno += t.value.count("\n")
45 return
46
47 def t_error(self, t):
48 raise Exception("Illegal character '%s'" % t.value[0])
49 t.lexer.skip(1)
50
51 def p_statement_scope(self, p):
52 '''S : SHOW STRING'''
53 self.arguments['key'] = p[2]
54
55 def p_error(self, p):
56 if p:
57 raise Exception("Syntax error at '%s'" % p.value)
58 else:
59 raise Exception("Syntax error : Incomplete Command")
60
61 def parse(self, shell):
62 yacc.parse(shell.line)
63 return self.arguments
064
=== added file 'src/mailmanclient/cli/client/parsers/subscribe.py'
--- src/mailmanclient/cli/client/parsers/subscribe.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/subscribe.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,84 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Subscribe(Parser):
29 tokens = ('SUBSCRIBE', 'USER', 'STRING', 'TO')
30 literals = ['+', '`', ',']
31 t_ignore = " \t"
32
33 def t_SUBSCRIBE(self, t):
34 r'subscribe'
35 return t
36
37 def t_USER(self, t):
38 '(user)s?'
39 return self.stem(t)
40 return t
41
42 def t_TO(self, t):
43 'to'
44 return t
45
46 def t_STRING(self, t):
47 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
48 t.value = t.value.replace('`', '')
49 return t
50
51 def t_newline(self, t):
52 r'\n+'
53 t.lexer.lineno += t.value.count("\n")
54 return
55
56 def t_error(self, t):
57 raise Exception("Illegal character '%s'" % t.value[0])
58 t.lexer.skip(1)
59
60 def p_statement_subscribe(self, p):
61 '''S : SUBSCRIBE USER USERLIST TO STRING'''
62 self.arguments['list'] = p[5]
63 self.arguments['scope'] = p[2]
64
65 def p_get_users(self, p):
66 '''USERLIST : STRING NEXT'''
67 if 'users' not in self.arguments:
68 self.arguments['users'] = []
69 self.arguments['users'].append(p[1])
70
71 def p_next(self, p):
72 '''NEXT : USERLIST
73 |'''
74 pass
75
76 def p_error(self, p):
77 if p:
78 raise Exception("Syntax error at '%s'" % p.value)
79 else:
80 raise Exception("Syntax error : Incomplete Command")
81
82 def parse(self, shell):
83 yacc.parse(shell.line)
84 return self.arguments
085
=== added file 'src/mailmanclient/cli/client/parsers/unset.py'
--- src/mailmanclient/cli/client/parsers/unset.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/unset.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,63 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Unset(Parser):
29 tokens = ('UNSET', 'STRING')
30 literals = ['=', '`']
31 t_ignore = " \t"
32
33 def t_UNSET(self, t):
34 r'unset'
35 return t
36
37 def t_STRING(self, t):
38 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
39 t.value = t.value.replace('`', '')
40 return t
41
42 def t_newline(self, t):
43 r'\n+'
44 t.lexer.lineno += t.value.count("\n")
45 return
46
47 def t_error(self, t):
48 raise Exception("Illegal character '%s'" % t.value[0])
49 t.lexer.skip(1)
50
51 def p_statement_scope(self, p):
52 '''S : UNSET STRING'''
53 self.arguments['key'] = p[2]
54
55 def p_error(self, p):
56 if p:
57 raise Exception("Syntax error at '%s'" % p.value)
58 else:
59 raise Exception("Syntax error : Incomplete Command")
60
61 def parse(self, shell):
62 yacc.parse(shell.line)
63 return self.arguments
064
=== added file 'src/mailmanclient/cli/client/parsers/unsubscribe.py'
--- src/mailmanclient/cli/client/parsers/unsubscribe.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/unsubscribe.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,83 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Unsubscribe(Parser):
29 tokens = ('UNSUBSCRIBE', 'USER', 'STRING', 'FROM')
30 literals = ['+', '`', ',']
31 t_ignore = " \t"
32
33 def t_UNSUBSCRIBE(self, t):
34 r'unsubscribe'
35 return t
36
37 def t_USER(self, t):
38 '(user)s?'
39 return self.stem(t)
40
41 def t_FROM(self, t):
42 'from'
43 return t
44
45 def t_STRING(self, t):
46 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
47 t.value = t.value.replace('`', '')
48 return t
49
50 def t_newline(self, t):
51 r'\n+'
52 t.lexer.lineno += t.value.count("\n")
53 return
54
55 def t_error(self, t):
56 raise Exception("Illegal character '%s'" % t.value[0])
57 t.lexer.skip(1)
58
59 def p_statement_unsubscribe(self, p):
60 '''S : UNSUBSCRIBE USER USERLIST FROM STRING'''
61 self.arguments['list'] = p[5]
62 self.arguments['scope'] = p[2]
63
64 def p_get_users(self, p):
65 '''USERLIST : STRING NEXT'''
66 if 'users' not in self.arguments:
67 self.arguments['users'] = []
68 self.arguments['users'].append(p[1])
69
70 def p_next(self, p):
71 '''NEXT : USERLIST
72 |'''
73 pass
74
75 def p_error(self, p):
76 if p:
77 raise Exception("Syntax error at '%s'" % p.value)
78 else:
79 raise Exception("Syntax error : Incomplete Command")
80
81 def parse(self, shell):
82 yacc.parse(shell.line)
83 return self.arguments
084
=== added file 'src/mailmanclient/cli/client/parsers/update.py'
--- src/mailmanclient/cli/client/parsers/update.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/parsers/update.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,114 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import ply.yacc as yacc
25from mailmanclient.cli.client.parsers.base import Parser
26
27
28class Update(Parser):
29 tokens = ('UPDATE', 'PREFERENCE', 'STRING', 'TO', 'WITH',
30 'AND', 'FOR', 'GLOBALLY', 'DOMAIN')
31 literals = ['=', '`']
32 t_ignore = " \t"
33
34 def t_UPDATE(self, t):
35 r'update'
36 return t
37
38 def t_PREFERENCE(self, t):
39 'preference'
40 return t
41
42 def t_WITH(self, t):
43 'with'
44 return t
45
46 def t_GLOBALLY(self, t):
47 'globally'
48 return t
49
50 def t_DOMAIN(self, t):
51 'user|address|member'
52 return t
53
54 def t_FOR(self, t):
55 'for'
56 return t
57
58 def t_TO(self, t):
59 'to'
60 return t
61
62 def t_AND(self, t):
63 'and'
64 return t
65
66 def t_STRING(self, t):
67 r'`([a-zA-Z0-9_@\.\*\-\$ ]*)`'
68 t.value = t.value.replace('`', '')
69 return t
70
71 def t_newline(self, t):
72 r'\n+'
73 t.lexer.lineno += t.value.count("\n")
74 return
75
76 def t_error(self, t):
77 raise Exception("Illegal character '%s'" % t.value[0])
78 t.lexer.skip(1)
79
80 def p_statement_update(self, p):
81 '''S : UPDATE PREFERENCE STRING TO STRING E'''
82 self.arguments['key'] = p[3]
83 self.arguments['value'] = p[5]
84
85 def p_domain(self, p):
86 '''E : GLOBALLY
87 | FOR DOMAIN WITH EXP'''
88 try:
89 self.arguments['scope'] = p[2]
90 except IndexError:
91 self.arguments['scope'] = p[1]
92
93 def p_exp_condition(self, p):
94 '''EXP : STRING "=" STRING CONJ'''
95 if 'filters' not in self.arguments:
96 self.arguments['filters'] = {}
97 self.arguments['filters'][p[1]] = p[3]
98
99 def p_conj_exp(self, p):
100 ''' CONJ : AND EXP
101 |'''
102 pass
103
104 def p_error(self, p):
105 if p:
106 raise Exception("Syntax error at '%s'" % p.value)
107 else:
108 raise Exception("Syntax error : Incomplete Command")
109
110 def parse(self, shell):
111 yacc.parse(shell.line)
112 if 'filters' not in self.arguments:
113 self.arguments['filters'] = {}
114 return self.arguments
0115
=== added file 'src/mailmanclient/cli/client/shell.py'
--- src/mailmanclient/cli/client/shell.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/client/shell.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,394 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24from cmd import Cmd
25from mailmanclient.cli.core.lists import Lists
26from mailmanclient.cli.core.domains import Domains
27from mailmanclient.cli.core.users import Users
28from mailmanclient.cli.core.preferences import Preferences
29from mailmanclient.cli.lib.mailman_utils import MailmanUtils
30from mailmanclient.cli.lib.utils import Filter
31
32utils = MailmanUtils()
33
34
35class Shell(Cmd):
36 intro = 'Mailman Command Line Interface'
37 prompt = '>>>'
38 env = {}
39 env_on = True
40 scope_classes = {}
41 scope_listing = {}
42 mmclient = None
43 scopes = ['list', 'user', 'domain']
44 line = None
45
46 def parseline(self, line):
47 """ This function sets the line attribute with the complete
48 command, which is used at the preprocessing stage"""
49 self.line = line
50 return Cmd.parseline(self, line)
51
52 def onecmd(self, s):
53 """ This method overides the Cmd.onecmd method, but eventully
54 calls the Cmd.onecmd function. The purpose of this function
55 is to catch the errors at a single point, rather than all
56 over the code.
57 """
58 try:
59 return Cmd.onecmd(self, s)
60 except Exception as e:
61 utils.error(e)
62 command = s.split()[0]
63 self.do_help(command)
64 return False
65
66 def emptyline(self):
67 """ Action on empty line entry. If not overridden, the shell executes last
68 command on encountering an empty line.
69 """
70 return False
71
72 def initialize(self):
73 """ Method to initialize the shell. Two hash tables are initialised, one
74 for storing the class signatures of the mailman Objects and the
75 other to store the lists of each object.
76 """
77 try:
78 self.mmclient = utils.connect()
79 self.scope_classes['list'] = Lists
80 self.scope_classes['domain'] = Domains
81 self.scope_classes['user'] = Users
82 self.scope_classes['preference'] = Preferences
83 self.refresh_lists()
84 except Exception as e:
85 utils.error(e)
86 exit(1)
87
88 def refresh_lists(self):
89 """ Refreshes the Mailman object list hash tables.
90 Invoke this method explicitly whenever the list contents might
91 get modified.
92 """
93 self.scope_listing['list'] = self.mmclient.lists
94 self.scope_listing['domain'] = self.mmclient.domains
95 self.scope_listing['user'] = self.mmclient.users
96
97 def do_EOF(self, args):
98 print()
99 print('Bye!')
100 exit(0)
101
102 def do_set(self, args):
103 """ Sets a variable in the environment
104 Usage:
105 set `<variable>` = `<value>`
106 """
107 from mailmanclient.cli.client.parsers._set import Set
108 parser = Set()
109 arguments = parser.parse(self)
110 key = arguments['key']
111 value = utils.add_shell_vars(arguments['value'], self)
112 self.env[key] = value
113 utils.warn('`%s` set to value `%s`' % (key, value))
114
115 def do_unset(self, args):
116 """ Delete a shell environment variable
117 Usage:
118 unset `<var_name>`"""
119 from mailmanclient.cli.client.parsers.unset import Unset
120 parser = Unset()
121 arguments = parser.parse(self)
122 key = arguments['key']
123 if key in self.env:
124 del self.env[key]
125 utils.warn('Shell Variable `%s` Deleted' % key)
126 else:
127 raise Exception('Invalid Argument `%s`' % key)
128
129 def do_show_var(self, args):
130 """ Show a shell environment variable
131 Usage:
132 show_var `<var_name>`"""
133 from mailmanclient.cli.client.parsers.show_var import ShowVar
134 parser = ShowVar()
135 arguments = parser.parse(self)
136 key = arguments['key']
137 if key in self.env:
138 utils.emphasize('Value of %s : %s' % (key, self.env[key]))
139 else:
140 raise Exception('Invalid Argument %s' % key)
141
142 def do_disable(self, args):
143 """ Disable the shell environment
144 Usage:
145 disable env"""
146 from mailmanclient.cli.client.parsers.disable import Disable
147 parser = Disable()
148 parser.parse(self)
149 self.env_on = False
150 utils.emphasize('Environment variables disabled')
151
152 def do_enable(self, args):
153 """ Enable the shell environment
154 Usage:
155 enable env"""
156 from mailmanclient.cli.client.parsers.enable import Enable
157 parser = Enable()
158 parser.parse(self)
159 self.env_on = True
160 utils.emphasize('Environment variables enabled')
161
162 def do_show(self, args):
163 """ Show requested mailman objects as a table
164 Usage:
165 show {domain|user|list} where `<object_attribute>` = `<value>`
166 show {domain|user|list} where `<object_attribute>` like `<regex>`
167 show {domain|user|list} where `<regex>` in `<list_attribute>`
168 show {domain|user|list} where <filter2> and <filter2> ..."""
169 from mailmanclient.cli.client.parsers.show import Show
170 parser = Show()
171 arguments = parser.parse(self)
172 scope = arguments['scope']
173 filtered_list = self.scope_listing[scope]
174 if 'filters' in arguments:
175 for i in arguments['filters']:
176 key, op, value = i
177 value = utils.add_shell_vars(value, self)
178 data_filter = Filter()
179 filtered_list = data_filter.get_results(key, value, op, filtered_list)
180 if not filtered_list:
181 return False
182 scope_object = self.scope_classes[scope](self.mmclient)
183 cmd_arguments = {}
184 if scope == 'list':
185 cmd_arguments['list'] = None
186 cmd_arguments['csv'] = None
187 cmd_arguments['domain'] = None
188 cmd_arguments['verbose'] = True
189 cmd_arguments['no_header'] = False
190 elif scope == 'domain':
191 cmd_arguments['domain'] = None
192 cmd_arguments['csv'] = None
193 cmd_arguments['verbose'] = True
194 cmd_arguments['no_header'] = False
195 elif scope == 'user':
196 cmd_arguments['user'] = None
197 cmd_arguments['csv'] = None
198 cmd_arguments['list_name'] = None
199 cmd_arguments['verbose'] = True
200 cmd_arguments['no_header'] = False
201 scope_object.show(cmd_arguments, filtered_list)
202
203 def do_create(self, args):
204 """ Creates mailman Objects
205 Usage:
206 create user with `email`=`EMAIL` and `password`=`PASSWD` and `name`=`NAME`
207 create domain with `name`=`DOMAIN` and `contact`=`CONTACT`
208 create list with `fqdn_listname`=`LIST`"""
209 from mailmanclient.cli.client.parsers.create import Create
210 parser = Create()
211 arguments = parser.parse(self)
212 scope = arguments['scope']
213 properties = arguments['properties']
214 scope_object = self.scope_classes[scope](self.mmclient)
215 cmd_arguments = {}
216 req_args = []
217 try:
218 if scope == 'list':
219 req_args = ['fqdn_listname']
220 cmd_arguments['list'] = utils.add_shell_vars(properties['fqdn_listname'], self)
221 elif scope == 'domain':
222 req_args = ['name', 'contact']
223 cmd_arguments['domain'] = utils.add_shell_vars(properties['name'], self)
224 cmd_arguments['contact'] = utils.add_shell_vars(properties['contact'], self)
225 elif scope == 'user':
226 req_args = ['email', 'password', 'name']
227 cmd_arguments['email'] = utils.add_shell_vars(properties['email'], self)
228 cmd_arguments['password'] = utils.add_shell_vars(properties['password'], self)
229 cmd_arguments['name'] = utils.add_shell_vars(properties['name'], self)
230 except KeyError:
231 utils.error('Invalid arguments')
232 utils.warn('The required arguments are:')
233 for i in req_args:
234 utils.warn('\t' + i)
235 return False
236 scope_object.create(cmd_arguments)
237 self.refresh_lists()
238
239 def do_delete(self, args):
240 """ Delete specified mailman objects
241 Usage:
242 delete {domain|user|list} where `<object_attribute>` = `<value>`
243 delete {domain|user|list} where `<object_attribute>` like `<regex>`
244 delete {domain|user|list} where `<key>` in `<list_attribute>`
245 delete {domain|user|list} where <filter1> and <filter2> ..."""
246 from mailmanclient.cli.client.parsers.delete import Delete
247 parser = Delete()
248 arguments = parser.parse(self)
249 scope = arguments['scope']
250 filtered_list = self.scope_listing[scope]
251 if 'filters' in arguments:
252 for i in arguments['filters']:
253 key, op, value = i
254 value = utils.add_shell_vars(value, self)
255 data_filter = Filter()
256 filtered_list = data_filter.get_results(key, value, op, filtered_list)
257 if 'filters' in arguments and arguments['filters'] == []:
258 utils.confirm('Delete all %ss?[y/n]' % scope)
259 ans = input()
260 if ans == 'n':
261 return False
262 for i in filtered_list:
263 if scope == 'list':
264 utils.warn('Deleted ' + i.fqdn_listname)
265 i.delete()
266 elif scope == 'domain':
267 utils.warn('Deleted ' + i.base_url)
268 self.mmclient.delete_domain(i.mail_host)
269 elif scope == 'user':
270 utils.warn('Deleted ' + i.display_name)
271 i.delete()
272 self.refresh_lists()
273
274 def do_subscribe(self, args):
275 """ Subscribes users to a list
276 Usage:
277 subscribe users `<email1>` `<email2>` ... to `<list fqdn_name>`"""
278 from mailmanclient.cli.client.parsers.subscribe import Subscribe
279 parser = Subscribe()
280 arguments = parser.parse(self)
281 users = arguments['users']
282 cleaned_list = []
283 for i in users:
284 cleaned_list.append(utils.add_shell_vars(i, self))
285 users = cleaned_list
286 list_name = utils.add_shell_vars(arguments['list'], self)
287 user_object = self.scope_classes['user'](self.mmclient)
288 cmd_arguments = {}
289 cmd_arguments['users'] = users
290 cmd_arguments['list_name'] = list_name
291 cmd_arguments['quiet'] = False
292 user_object.subscribe(cmd_arguments)
293 self.refresh_lists()
294
295 def do_unsubscribe(self, args):
296 """ Unsubscribes users from a list
297 Usage:
298 unsubscribe users `<email1>` [`<email2>` ...] from `<list fqdn_name>`
299 """
300 from mailmanclient.cli.client.parsers.unsubscribe import Unsubscribe
301 parser = Unsubscribe()
302 arguments = parser.parse(self)
303 users = arguments['users']
304 cleaned_list = []
305 for i in users:
306 cleaned_list.append(utils.add_shell_vars(i, self))
307 users = cleaned_list
308 list_name = utils.add_shell_vars(arguments['list'], self)
309 user_object = self.scope_classes['user'](self.mmclient)
310 cmd_arguments = {}
311 cmd_arguments['users'] = users
312 cmd_arguments['list_name'] = list_name
313 cmd_arguments['quiet'] = False
314 user_object.unsubscribe(cmd_arguments)
315 self.refresh_lists()
316
317 def do_update(self, args):
318 """ Command to set preferences
319 Usage:
320
321 update preference `<preference_name>` to `<value>` for member with
322 `email` = `foo@bar.com`
323 and `list` = `list@domain.org`
324
325 update preference `<preference_name>` to `<value>`
326 for user with `email` = `foo@bar.com`
327
328 update preference `<preference_name>` to `<value>`
329 for address with `email` = `foo@bar.com`"""
330 from mailmanclient.cli.client.parsers.update import Update
331 parser = Update()
332 arguments = parser.parse(self)
333 scope = arguments['scope']
334 preferences = self.scope_classes['preference'](self.mmclient)
335 cmd_arguments = {}
336 cmd_arguments['key'] = arguments['key']
337 cmd_arguments['value'] = arguments['value']
338 if scope == 'globally':
339 cmd_arguments['update_scope'] = 'global'
340 else:
341 cmd_arguments['update_scope'] = scope
342 for i in arguments['filters']:
343 cmd_arguments[i] = utils.add_shell_vars(arguments['filters'][i], self)
344 preferences.update(cmd_arguments)
345
346 def _complete(self, text, keys):
347 """ Method for computing the auto suggest suggestions
348 """
349 if not text:
350 completions = keys
351 else:
352 completions = [k
353 for k in keys
354 if k.startswith(text)
355 ]
356 return completions
357
358 def complete_set(self, text, line, begidx, endidx):
359 keys = self.env.keys()
360 keys.extend(self.scopes)
361 return self._complete(text, keys)
362
363 def complete_unset(self, text, line, begidx, endidx):
364 return self._complete(text, self.env.keys())
365
366 def complete_show_var(self, text, line, begidx, endidx):
367 return self._complete(text, self.env.keys())
368
369 def complete_delete(self, text, line, begidx, endidx):
370 return self._complete(text, self.scopes)
371
372 def complete_create(self, text, line, begidx, endidx):
373 return self._complete(text, self.scopes)
374
375 def complete_show(self, text, line, begidx, endidx):
376 return self._complete(text, self.scopes)
377
378 def complete_subscribe(self, text, line, begidx, endidx):
379 return self._complete(text, ['user'])
380
381 def complete_unsubscribe(self, text, line, begidx, endidx):
382 return self._complete(text, ['user'])
383
384 def complete_disable(self, text, line, begidx, endidx):
385 disable_list = ['env']
386 return self._complete(text, disable_list)
387
388 def complete_enable(self, text, line, begidx, endidx):
389 enable_list = ['env']
390 return self._complete(text, enable_list)
391
392 def complete_update(self, text, line, begidx, endidx):
393 _list = ['preference']
394 return self._complete(text, _list)
0395
=== added directory 'src/mailmanclient/cli/core'
=== added file 'src/mailmanclient/cli/core/__init__.py'
=== added file 'src/mailmanclient/cli/core/domains.py'
--- src/mailmanclient/cli/core/domains.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/core/domains.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,136 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24from tabulate import tabulate
25from six.moves.urllib_error import HTTPError
26from mailmanclient.cli.lib.utils import Utils
27
28
29utils = Utils()
30
31
32class DomainException(Exception):
33 """ Exception on invalid domain """
34 pass
35
36
37class Domains():
38 """Domain related actions."""
39
40 def __init__(self, client):
41 self.client = client
42
43 def create(self, args):
44 """Create a domain name with specified domain_name.
45 Optionally, the contact address can also be specified.
46
47 :param args: Commandline arguments
48 :type args: dictionary
49 """
50 domain_name = args['domain']
51 contact_address = args['contact']
52
53 try:
54 self.client.create_domain(domain_name,
55 contact_address=contact_address)
56 except HTTPError as e:
57 code = e.getcode()
58 if code == 400:
59 raise DomainException('Domain already exists')
60 else:
61 raise DomainException('An unknown HTTPError has occured')
62
63 def show(self, args, domains_ext=None):
64 """List the domains in the system.
65
66 :param args: Commandline arguments
67 :type args: dictionary
68 """
69 if args['domain'] is not None:
70 self.describe(args)
71 return
72
73 headers = []
74 fields = ['base_url']
75 domains = []
76
77 if domains_ext:
78 domains = domains_ext
79 else:
80 domains = self.client.domains
81
82 if not args['no_header'] and args['verbose']:
83 headers = ['Base URL', 'Contact address',
84 'Mail host', 'URL host']
85
86 if args['verbose']:
87 fields = ['base_url', 'contact_address', 'mail_host', 'url_host']
88
89 table = utils.get_listing(domains, fields)
90
91 if args['csv']:
92 utils.write_csv(table, headers, args['csv'])
93 else:
94 print(tabulate(table, headers=headers, tablefmt='plain'))
95
96 def describe(self, args):
97 try:
98 domain = self.client.get_domain(args['domain'])
99 except HTTPError as e:
100 code = e.getcode()
101 if code == 404:
102 raise DomainException('Domain not found')
103 else:
104 raise DomainException('An unknown HTTPError has occured')
105 table = []
106 table.append(['Base URL', domain.base_url])
107 table.append(['Contact Address', domain.contact_address])
108 table.append(['Mail Host', domain.mail_host])
109 table.append(['URL Host', domain.url_host])
110 utils.set_table_section_heading(table, 'Description')
111 table.append([domain.description, ''])
112 utils.set_table_section_heading(table, 'Lists')
113 for _list in domain.lists:
114 table.append([_list.list_id, ''])
115 print(tabulate(table, tablefmt='plain'))
116
117 def delete(self, args):
118 try:
119 domain = self.client.get_domain(args['domain'])
120 except HTTPError as e:
121 code = e.getcode()
122 if code == 404:
123 raise DomainException('Domain not found')
124 else:
125 raise DomainException('An unknown HTTPError has occured')
126 if not args['yes']:
127 utils.confirm('Domain `%s` has %d lists.Delete?[y/n]'
128 % (args['domain'], len(domain.lists)))
129 confirm = input()
130 if confirm == 'y':
131 args['yes'] = True
132 elif confirm == 'n':
133 return
134 else:
135 raise Exception('Invalid answer')
136 self.client.delete_domain(args['domain'])
0137
=== added file 'src/mailmanclient/cli/core/lists.py'
--- src/mailmanclient/cli/core/lists.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/core/lists.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,227 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24from tabulate import tabulate
25from six.moves.urllib_error import HTTPError
26from mailmanclient.cli.lib.utils import Utils
27from mailmanclient.cli.core.domains import DomainException
28
29
30utils = Utils()
31
32
33class ListException(Exception):
34 """ List Exceptions """
35 pass
36
37
38class Lists():
39
40 """Mailing list related actions."""
41
42 def __init__(self, client):
43 self. client = client
44
45 def create(self, args):
46 """Create a mailing list with specified list_name
47 in the domain specified by domain_name.
48
49 :param args: Commandline arguments
50 :type args: dictionary
51 """
52 name = args['list'].split('@')
53
54 try:
55 list_name = name[0]
56 domain_name = name[1]
57 except IndexError:
58 raise ListException('Invalid FQDN list name')
59
60 if list_name.strip() == '' or domain_name.strip() == '':
61 raise ListException('Invalid FQDN list name')
62
63 domain = self.get_domain(domain_name)
64
65 try:
66 domain.create_list(list_name)
67 except HTTPError as e:
68 code = e.getcode()
69 if code == 400:
70 raise ListException('List already exists')
71 else:
72 raise Exception('An unknown HTTPError has occoured')
73
74 def show(self, args, lists_ext=None):
75 """List the mailing lists in the system or under a domain.
76
77 :param args: Commandline arguments
78 :type args: dictionary
79 """
80 if args['list'] is not None:
81 self.describe(args)
82 return
83
84 lists = []
85 fields = ['list_id']
86 headers = []
87
88 if args['domain']:
89 domain = self.get_domain(args['domain'])
90 lists = domain.lists
91 elif lists_ext:
92 lists = lists_ext
93 else:
94 lists = self.client.lists
95
96 if args['verbose']:
97 fields = ['list_id', 'list_name',
98 'mail_host', 'display_name',
99 'fqdn_listname']
100
101 if not args['no_header'] and args['verbose']:
102 headers = ['ID', 'Name', 'Mail host', 'Display Name', 'FQDN']
103
104 table = utils.get_listing(lists, fields)
105
106 if args['csv']:
107 utils.write_csv(table, headers, args['csv'])
108 else:
109 print(tabulate(table, headers=headers, tablefmt='plain'))
110
111 def describe(self, args):
112 _list = self.get_list(args['list'])
113 table = []
114 table.append(['List ID', _list.list_id])
115 table.append(['List name', _list.list_name])
116 table.append(['Mail host', _list.mail_host])
117 utils.set_table_section_heading(table, 'List Settings')
118 for i in _list.settings:
119 table.append([i, str(_list.settings[i])])
120 utils.set_table_section_heading(table, 'Owners')
121 for owner in _list.owners:
122 table.append([owner, ''])
123 utils.set_table_section_heading(table, 'Moderators')
124 for moderator in _list.moderators:
125 table.append([moderator, ''])
126 utils.set_table_section_heading(table, 'Members')
127 for member in _list.members:
128 email = member.address.split('/')[-1]
129 table.append([email, ''])
130 print(tabulate(table, tablefmt='plain'))
131
132 def add_moderator(self, args):
133 _list = self.get_list(args['list'])
134 users = args['users']
135 quiet = args['quiet']
136 for user in users:
137 try:
138 _list.add_moderator(user)
139 if not quiet:
140 utils.warn('Added %s as moderator' % (user))
141 except Exception as e:
142 if not quiet:
143 utils.error('Failed to add %s : %s ' %
144 (user, e))
145
146 def add_owner(self, args):
147 _list = self.get_list(args['list'])
148 users = args['users']
149 quiet = args['quiet']
150 for user in users:
151 try:
152 _list.add_owner(user)
153 if not quiet:
154 utils.warn('Added %s as owner' % (user))
155 except Exception as e:
156 if not quiet:
157 utils.error('Failed to add %s : %s ' %
158 (user, e))
159
160 def remove_moderator(self, args):
161 _list = self.get_list(args['list'])
162 users = args['users']
163 quiet = args['quiet']
164 for user in users:
165 try:
166 _list.remove_moderator(user)
167 if not quiet:
168 utils.warn('Removed %s as moderator' % (user))
169 except Exception as e:
170 if not quiet:
171 utils.error('Failed to remove %s : %s ' %
172 (user, e))
173
174 def remove_owner(self, args):
175 _list = self.get_list(args['list'])
176 users = args['users']
177 quiet = args['quiet']
178 for user in users:
179 try:
180 _list.remove_owner(user)
181 if not quiet:
182 utils.warn('Removed %s as owner' % (user))
183 except Exception as e:
184 if not quiet:
185 utils.error('Failed to remove %s : %s ' %
186 (user, e))
187
188 def show_members(self, args):
189 from core.users import Users
190 users = Users(self.client)
191 args['list_name'] = args['list']
192 args['user'] = None
193 users.show(args)
194
195 def delete(self, args):
196 _list = self.get_list(args['list'])
197 if not args['yes']:
198 utils.confirm('List %s has %d members.Delete?[y/n]'
199 % (args['list'], len(_list.members)))
200 confirm = input()
201 if confirm == 'y':
202 args['yes'] = True
203 elif confirm == 'n':
204 return
205 else:
206 raise Exception('Invalid Answer')
207 _list.delete()
208
209 def get_list(self, listname):
210 try:
211 return self.client.get_list(listname)
212 except HTTPError as e:
213 code = e.getcode()
214 if code == 404:
215 raise ListException('List not found')
216 else:
217 raise Exception('An unknown HTTPError has occoured')
218
219 def get_domain(self, domainname):
220 try:
221 return self.client.get_domain(domainname)
222 except HTTPError as e:
223 code = e.getcode()
224 if code == 404:
225 raise DomainException('Domain not found')
226 else:
227 raise Exception('An unknown HTTPError has occoured')
0228
=== added file 'src/mailmanclient/cli/core/misc.py'
--- src/mailmanclient/cli/core/misc.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/core/misc.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,69 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24import os
25import zipfile
26from mailman.config import config
27from mailmanclient.cli.lib.utils import Utils
28
29
30utils = Utils()
31
32
33class MiscException(Exception):
34 """ Exceptions for miscellaneous actions """
35 pass
36
37
38class Misc():
39 """ Miscellaneous actions """
40
41 def backup(self, args):
42 config.load()
43 vardir = config.paths['VAR_DIR']
44 output = args['output']
45 if not output.endswith('.zip'):
46 output += '.zip'
47 relroot = os.path.abspath(os.path.join(vardir, os.pardir))
48 with zipfile.ZipFile(output, "w", zipfile.ZIP_DEFLATED) as z:
49 for root, dirs, files in os.walk(vardir):
50 z.write(root, os.path.relpath(root, relroot))
51 for f in files:
52 filename = os.path.join(root, f)
53 if os.path.isfile(filename):
54 arcname = os.path.join(os.path.relpath(root, relroot),
55 f)
56 z.write(filename, arcname)
57
58 def restore(self, args):
59 config.load()
60 vardir = config.paths['VAR_DIR']
61 backup = args['backup']
62 if os.path.exists(vardir):
63 utils.confirm('The existing var_dir will be replaced.Continue?[y/n]')
64 confirm = input()
65 if not confirm == 'y':
66 return
67 vardir += '/../'
68 with zipfile.ZipFile(backup) as zf:
69 zf.extractall(vardir)
070
=== added file 'src/mailmanclient/cli/core/preferences.py'
--- src/mailmanclient/cli/core/preferences.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/core/preferences.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,96 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24from mailmanclient.cli.lib.utils import Utils
25
26
27utils = Utils()
28
29
30class PreferenceException(Exception):
31 pass
32
33
34class Preferences():
35 """Preferences related actions."""
36
37 def __init__(self, client):
38 self.client = client
39
40 def get_scope_object(self, scope, args):
41 scope_object = None
42 try:
43 if scope == 'global':
44 scope_object = self.client
45 elif scope == 'user':
46 scope_object = self.client.get_user(args['email'])
47 elif scope == 'member':
48 scope_object = self.client.get_member(args['list'],
49 args['email'])
50 else:
51 scope_object = self.client.get_address(args['email'])
52 except:
53 raise PreferenceException('%s not found' % scope.capitalize())
54 return scope_object
55
56 def update(self, args):
57 """Update a preference specified by the `key` to `value`
58 Preferences can be set at a global, user, address or at
59 a member level.
60 """
61 scope = args['update_scope']
62 if scope == 'global':
63 raise PreferenceException('Global preferences are readonly')
64 scope_object = self.get_scope_object(scope, args)
65 preferences = None
66 key = args['key']
67 value = args['value']
68 preferences = scope_object.preferences
69 try:
70 preferences[key]
71 except Exception:
72 raise PreferenceException('Saving Preference Failed')
73 if type(preferences[key]).__name__ in ('bool', 'NoneType'):
74 value = value.lower().strip()
75 if value == 'true':
76 value = True
77 elif value == 'false':
78 value = False
79 else:
80 raise PreferenceException('Invalid value for preference.'
81 'Expected values : True/False')
82 try:
83 preferences[key] = value
84 preferences.save()
85 except Exception:
86 raise PreferenceException('Saving Preference Failed')
87
88 def show(self, args):
89 """Given a preference key, and a specific object, print
90 the current value of the preference for that object."""
91 scope = args['show_scope']
92 scope_object = self.get_scope_object(scope, args)
93 preferences = None
94 key = args['key']
95 preferences = scope_object.preferences
96 print(str(preferences[key]))
097
=== added file 'src/mailmanclient/cli/core/users.py'
--- src/mailmanclient/cli/core/users.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/core/users.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,189 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24from tabulate import tabulate
25from six.moves.urllib_error import HTTPError
26from mailmanclient.cli.lib.utils import Utils
27from mailmanclient.cli.core.lists import ListException
28
29utils = Utils()
30
31
32class UserException(Exception):
33 """ User Exceptions """
34 pass
35
36
37class Users():
38
39 """User related actions."""
40
41 def __init__(self, client):
42 self.client = client
43
44 def create(self, args):
45 """Create a user with specified email,password and display name.
46
47 :param args: Commandline arguments
48 :type args: dictionary
49 """
50 email = args['email']
51 password = args['password']
52 display_name = args['name']
53
54 try:
55 self.client.create_user(email=email,
56 password=password,
57 display_name=display_name)
58 except HTTPError as e:
59 code = e.getcode()
60 if code == 400:
61 raise UserException('User already exists')
62 else:
63 raise UserException('An unknown HTTPError has occured')
64
65 def show(self, args, users_ext=None):
66 """List users in the system.
67
68
69 :param args: Commandline arguments
70 :type args: dictionary
71 """
72 if args['user'] is not None:
73 self.describe(args)
74 return
75
76 headers = []
77 fields = ['addresses']
78 users = []
79
80 if args['verbose']:
81 fields = ['display_name', 'addresses', 'created_on', 'user_id']
82
83 if not args['no_header'] and args['verbose']:
84 headers = ['Display Name', 'Address', 'Created on', 'User ID']
85
86 if args['list_name']:
87 users = self.get_users(args['list_name'])
88 elif users_ext:
89 users = users_ext
90 else:
91 users = self.client.users
92
93 table = utils.get_listing(users, fields)
94
95 if args['csv']:
96 utils.write_csv(table, headers, args['csv'])
97 else:
98 print(tabulate(table, headers=headers, tablefmt='plain'))
99
100 def get_users(self, listname):
101 users = []
102 _list = self.client.get_list(listname)
103 for member in _list.members:
104 users.append(member.user)
105 return users
106
107 def describe(self, args):
108 ''' Describes a user object '''
109 user = self.get_user(args['user'])
110 table = []
111 table.append(['User ID', user.user_id])
112 table.append(['Display Name', user.display_name])
113 table.append(['Created on', user.created_on])
114 table.append(['Self Link', user.self_link])
115 utils.set_table_section_heading(table, 'User Preferences')
116 preferences = user.preferences._preferences
117 for i in preferences:
118 table.append([i, str(preferences[i])])
119 utils.set_table_section_heading(table, 'Subscription List IDs')
120 for _list in user.subscription_list_ids:
121 table.append([_list, ''])
122 utils.set_table_section_heading(table, 'Subscriptions')
123 for subscription in user.subscriptions:
124 email = subscription.address.split('/')[-1]
125 table.append([email+' at '+str(subscription.list_id),
126 str(subscription.role)])
127 print(tabulate(table, tablefmt='plain'))
128
129 def delete(self, args):
130 ''' Deletes a User object '''
131 user = self.client.get_user(args['user'])
132 if not args['yes']:
133 utils.confirm('Delete user %s?[y/n]' % args['user'])
134 confirm = input()
135 if confirm == 'y':
136 args['yes'] = True
137 elif confirm == 'n':
138 return
139 else:
140 raise Exception('Invalid answer')
141 user.delete()
142
143 def subscribe(self, args):
144 ''' Subsribes a user or a list of users to a list '''
145 list_name = args['list_name']
146 emails = args['users']
147 _list = self.client.get_list(list_name)
148 for i in emails:
149 try:
150 _list.subscribe(i)
151 if not args['quiet']:
152 utils.warn('%s subscribed to %s' % (i, list_name))
153 except Exception as e:
154 if not args['quiet']:
155 utils.error('Failed to subscribe %s : %s' % (i, e))
156
157 def unsubscribe(self, args):
158 ''' Unsubsribes a user or a list of users from a list '''
159 list_name = args['list_name']
160 emails = args['users']
161 _list = self.client.get_list(list_name)
162 for i in emails:
163 try:
164 _list.unsubscribe(i)
165 if not args['quiet']:
166 utils.warn('%s unsubscribed from %s' % (i, list_name))
167 except Exception as e:
168 if not args['quiet']:
169 utils.error('Failed to unsubscribe %s : %s' % (i, e))
170
171 def get_list(self, listname):
172 try:
173 return self.client.get_list(listname)
174 except HTTPError as e:
175 code = e.getcode()
176 if code == 404:
177 raise ListException('List not found')
178 else:
179 raise ListException('An unknown HTTPError has occured')
180
181 def get_user(self, username):
182 try:
183 return self.client.get_user(username)
184 except HTTPError as e:
185 code = e.getcode()
186 if code == 404:
187 raise UserException('User not found')
188 else:
189 raise UserException('An unknown HTTPError has occured')
0190
=== added directory 'src/mailmanclient/cli/docs'
=== added file 'src/mailmanclient/cli/docs/using_cli_shell.txt'
--- src/mailmanclient/cli/docs/using_cli_shell.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/docs/using_cli_shell.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,150 @@
1The Mailman Command Line Shell
2******************************
3
4This document describes the usage of the Mailman Command line
5shell, using which you can query a mailman installation with ease.
6
7Firing and Exiting the Shell
8============================
9
10You can start the mailman shell by executing the mmclient command,
11without any arguments::
12
13 $ ./mmclient
14 Mailman Command Line Interface
15 >>>
16
17To exit the shell, use the ``EOF`` character, that is, ``Ctrl + d``.
18
19Displaying Mailman Objects
20==========================
21
22The shell can be used to display the mailman objects, using the show
23command.
24
25For example::
26
27 >>> show users
28 >>> show domains
29 >>> show lists
30
31Further, the CLI supports filtering of mailman objects based upon their
32attribute values or properties, using a `where` clause. For this, the CLI
33employs 3 filters, which are::
34
35 = Equality
36 like Case insensitive regular exp mathcing
37 in Search inside a property which list
38
39These filteres can be used in conjunction by using an ``and`` clause
40
41Examples: ::
42
43 >>> show users where `display_name` = `Foo`
44 >>> show users where `display_name` like `.*Foo*`
45 >>> show lists where `foo@bar.com` in `moderators`
46 >>> show lists where `foo@bar.com` in `moderators` and `a@b.com` in `owners`
47
48The Shell Environment
49======================
50
51The shell provides a facility to create variables that can be used to
52make the querying easier.
53
54For using the shell, two commands, ``set`` and ``unset`` are used.
55
56Example::
57
58 >>> set `useremail` = `foo@bar.com`
59
60The variables can be used in the queries as follows::
61
62 >>> show lists where `$useremail` in `moderators`
63
64The ``$username`` will be replaced with the value of ``useremail``
65
66The environment can be disabled using the `disable environemt`
67command, that prevents the CLI from replacing the query terms
68with environment variables, or appending of the specialised
69variables.
70
71The disabled environment can be enabled using the `enable env`
72command.::
73
74 >>> disable env
75 >>> enable env
76
77The environment supports a set of special variables, which denote
78the names of the scopes available in mailman. They are domain, list and
79user.
80
81The special environment variables are appended automatically with relevant commands
82
83For example, if the environment variable domain is set to a domain name, then the
84`show list ` command automatically applies a domain = <set domain> filter to
85the result list.::
86
87 >>> set `domain` = `domain.org`
88 >>> show lists //Shows lists under domain.org
89 >>> disable env
90 >>> show lists //Shows all lists
91 >>> enable env
92
93The value of stored variables can be viewed using the show_var command::
94
95 >>> show_var `domain`
96
97Create Objects
98==============
99
100The Mailman objects can be created using the `create` command
101
102The create command accepts the object properties and creates
103the object.
104
105If the supplied arguments are invalid or insufficient, the list
106of arguments that are required are displayed.
107
108The create command can be used as follows::
109
110 >>> create list where `fqdn_listname` = `list@domain.org`
111 >>> create domain where `domain` = `domain.org` and `contact` = `a@b.com`
112 >>> create user where `email` = `foo@bar.com` and `password` = `a` and `name` = `Foo`
113
114Delete Objects
115==============
116
117The Mailman objects can be deleted using the delete command. The
118delete command supports the same filters as those by the show command.
119
120For example::
121
122 >>> delete domain where `domain` like `test_.*`
123
124Subscription
125============
126
127The subscription commands include two commands, subscribe and
128unsubscribe users, which are respectively used to subscribe users to a
129list and unsubscribe users from a list. The commands allow applying
130the action on a single user or multiple users at a time.::
131
132 >>> subscribe users `a@b.com` `foo@bar.com` to `list@domain.org`
133 >>> unsubscribe users `a@b.com` `foo@bar.com` from `list@domain.org`
134
135Update Preferences
136==================
137
138Preferences can be updated using the shell for the following domains
139 - Users
140 - Members
141 - Addresses
142
143The actions are performed using the update command which can be used as follows::
144
145 >>> update preference `<preference_name>` to `<value>` for member with `email` = `foo@bar.com`
146 and `list` = `list@domain.org`
147 >>> update preference `<preference_name>` to `<value>` for user with `email` = `foo@bar.com`
148 >>> update preference `<preference_name>` to `<value>` for address with `email` = `foo@bar.com`
149
150Global preferences are readonly.
0151
=== added file 'src/mailmanclient/cli/docs/using_cli_tools.txt'
--- src/mailmanclient/cli/docs/using_cli_tools.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/docs/using_cli_tools.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,311 @@
1The Mailman Command Line Tools
2******************************
3
4Initialization
5==============
6
7The CLI can be started by running mmclient [options|arguments]
8
9If the mmclient is run without any arguments, the shell
10is started, else the specified action is performed. Use EOF
11to exit the shell.::
12
13 $> mmclient
14 Mailman Command Line Interface v1.0
15 >>>
16 Bye!
17
18 $> mmclient [options]
19
20If you have non-default login credentials, specify them with the
21following options::
22
23 --host HOSTNAME [defaults to http://127.0.0.1]
24 --port PORTNUMBER [defaults to 8001]
25 --restuser USERNAME [defaults to restadmin]
26 --restpass PASSWORD [defaults to restpass]
27
28Domains
29=======
30
31This section describes the domain related functions that can be performed
32with the CLI.
33
34Create a new domain
35-------------------
36
37To create a new domain::
38
39 $> mmclient create domain testdomain.org
40
41List Domains
42------------
43To list the domains in the system::
44
45 $> mmclient show domain
46 http://domain.org
47
48To obtain a detailed listing, use the `-v`/`--verbose` switch.
49The detailed listing of domains displays the domains as a table::
50
51 $> mmclient show domain -v
52 Base URL Contact address Mail host URL host
53 http://domain.org postmaster@domain.org domain.org domain.org
54
55In addition, the long listing has a `no-header` switch that can be used
56to remove the header, making it more comfortable to pipe the output.::
57
58 $> mmclient show domain -v --no-header
59 http://domain.org postmaster@domain.org domain.org domain.org
60
61Delete a domain
62---------------
63
64To delete a domian ::
65 $> mmclient delete domain domain.org
66
67To supress the confirmation message::
68
69 $> mmclient delete domain domain.org --yes
70
71To obtain a detailed description of a domain at one glance::
72
73 $> mmclient show domain domain.org
74
75Mailing List
76============
77
78This section describes the mailing list related function that can be
79performed with the CLI.
80
81Create a mailing list
82---------------------
83
84To create a mailing list::
85
86 $> mmclient create list list@domain.org
87
88Show Mailing lists
89-------------------
90
91Show all mailing lists in the system::
92
93 $> mmclient show list
94 foo.domain.org
95 bar.example.org
96
97To display the lists under the domain `domain.org`::
98
99 $> mmclient show list -d domain.org
100 foo.domain.org
101
102Further, a switch -v/--verbose can also be used to print a detailed listing::
103
104 $> mmclient show list --verbose
105 ID Name Mail host Display Name FQDN
106 list.domain.org list domain.org List list@domain.org
107
108Again, the `show list` supports a `--no-header` switch that removes
109the header line of the long listing::
110
111 $> mmclient show list --verbose --no-header
112 list.domain.org list domain.org List list@domain.org
113
114Delete Mailing list
115-------------------
116
117To delete a list::
118
119 $> mmclient delete list list@domain.org
120
121To supress the confirmation message, do::
122
123 $> mmclient delete list list@domain.org --yes
124
125To obtain a detailed description of a list at one glance::
126
127 $> mmclient show list list@domain.org
128
129Manage list owners, members and moderators::
130---------------------------------
131
132Adding and removing moderators can be performed using the CLI as follows
133
134To get a list of members of a mailing list::
135
136 $> mmclient show-members list list@domain.org
137
138The above command is equivalent to::
139
140 $> mmclient show user --list list@domain.com
141
142The command also supports flags like::
143
144 --verbose Show a detailed listing
145 --no-header Hide header in detailed listing
146
147Refer the `show user` command for more details
148
149Add or remove list owners and moderators
150-----------------------------------
151
152To add moderators or owners::
153
154 $> mmclient add-moderator list list@domain.org --user a@b.com b@c.com
155 $> mmclient add-owner list list@domain.org --user a@b.com b@c.com
156
157And to remove moderators or owners::
158
159 $> mmclient remove-moderator list list@domain.org --user a@b.com b@c.com
160 $> mmclient remove-owner list list@domain.org --user a@b.com b@c.com
161
162The add and remove commands support the action on a list of users
163at once. Success or failure messages are provided upon completion or
164failure of an action. The messages can be supressed by using a quiet flag::
165
166 $> mmclient remove-moderator list list@domain.org --user a@b.com b@c.com --quiet
167
168If the action fails for a user, the user is ignored and the action continues
169without stalling.
170
171User
172====
173
174Craete User
175-----------
176
177To create a new user::
178
179 $> mmclient create user foo@bar.com --name "Foo" --password "password"
180
181The `show user` command lists a single email ID of each user::
182
183 $> mmclient show user
184 foo@bar.com
185
186To list users who are members of a list::
187
188 $> mmclient show user --list list@domain.org
189 foo@bar.com
190
191The show command also supports a `--verbose` switch ::
192
193 $> mmclient show user --verbose
194 Display Name Address Created on User ID
195 Foo foo2@bar.com 2014-05-30T00:52:52.564634 220337223817757552725201672981303248133
196
197and a `--no-header` switch::
198
199 $> mmclient show user --verbose --no-header
200 Foo foo2@bar.com 2014-05-30T00:52:52.564634 220337223817757552725201672981303248133
201
202Delete User
203-----------
204
205To delete a user::
206
207 $> mmclient delete user foo@bar.com
208
209To supress the confirmation message, do::
210
211 $> mmclient delete user foo@bar.com --yes
212
213Describe user
214-------------
215
216To obtain a detailed description of a user at one glance::
217
218 $> mmclient show user foo@bar.com
219
220Subscription and Unsubscription
221--------------------------------
222
223Users can be subscribed to a mailing list by using the subscribe
224command::
225
226 $> mmclient subscribe user user1@bar.com user2@bar.com --list list@domain.org
227 user1@bar.com subscribed to list@domain.org
228 Failed to subscribe user2@bar.com : HTTP Error 409: Member already subscribed
229
230Multiple users can be subscribed to the list at the same time.
231
232Similarly, Users can be unsubscribed to a mailing list by using the unsubscribe
233command::
234
235 $> mmclient unsubscribe user user1@bar.com user2@bar.com --list list@domain.org
236 user1@bar.com unsubscribed from list@domain.org
237 user2@bar.com unsubscribed from list@domain.org
238
239The feedback for the subscribe and unsubscribe actions can be supressed by
240using the --quiet flag::
241
242 $> mmclient subscribe user user1@bar.com user2@bar.com --list list@domain.org --quiet
243 $> mmclient unsubscribe user user1@bar.com user2@bar.com --list list@domain.org --quiet
244
245The subscribe and unsubscribe actions continue even if one the users
246fail to subscribe/unsubscribe to a list. A relevant feedback message is
247provided if the --quiet flag is not enabled.
248
249Preferences
250===========
251
252
253Update Preference
254-----------------
255
256Preferences for user,address,member or globally can be updated and retrieved
257by using the commands of preference scope.
258
259To update the value of a preference of an user::
260
261 $> mmclient update preference user --email foo@bar.com [key] [value]
262
263To update the value of a preference of a member::
264
265 $> mmclient update preference member --email foo@bar.com --list list@domain.org [key] [value]
266
267To update the value of a preference of an address::
268
269 $> mmclient update preference address --email foo@bar.com [key] [value]
270
271To update the value of a preference globally::
272
273 $> mmclient update preference global [key] [value]
274
275View Setting
276------------
277To view the current value of a preference, use the `mmclient show preference` command
278in the same way above, obviously, without the `value` argument
279
280For eg, to view a setting of a member::
281
282 $> mmclient show preference member --email foo@bar.com --list list@domain.org [key]
283
284Both the commands try to suggest the possible preference keys upon errors while typing the
285keys. The commands return `1` upon an invalid key.
286
287Backup and Restore
288==================
289
290The CLI tools can be used to create backups and restore the backups of the
291Mailman data. The backup currently supports the backup for SQLite mode.
292
293Backup
294------
295
296The backup tools backs up the $var_dir specified by the Mailman configuration
297as a zip archive to a specified location::
298
299 $> mmclient backup ~/backup.zip
300
301Restore
302-------
303To restore the backup, specfiy the path to the backup file::
304
305 $> mmclient restore ~/backup.zip
306
307Please remember to stop the mailman runner before performing
308the backup and restore operations.
309
310The paths for backup and restore are read from the Mailman configuration
311file.
0312
=== added file 'src/mailmanclient/cli/docs/writing_a_new_command.txt'
--- src/mailmanclient/cli/docs/writing_a_new_command.txt 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/docs/writing_a_new_command.txt 2015-03-17 19:08:34 +0000
@@ -0,0 +1,57 @@
1Writing a new command
2*********************
3
4There are two types of commands in the CLI project, either add a new command
5to the CLI tools, that gives a normal terminal command, or build a command
6for the Mailman custom shell.
7
8Writing a new command for Command tools
9=======================================
10
11To write a new command for the command line tools, the following
12steps are to be followed.
13
14- Decide a command name and arguments expected
15- Decide whether the command comes under a scope. Currently
16the following scopes are supported
17
18 - users
19 - domains
20 - preferences
21 - lists
22
23The new command can either be under any of these scopes or
24can exist independently without a scope.
25
26- If the new command BAR is to be added to an existing scope ``FOO``,
27 the command would look like ``FOO BAR``, where ``FOO`` is the action and
28 BAR is the scope
29- Add a new parser to the `action` parser as::
30
31 new_cmd = action.add_parser('FOO')
32
33 Add the scope as follows::
34
35 scope = new_cmd.add_subparser(dest='scope')
36 FOO_BAR = scope.add_parser('BAR')
37
38- Add the arguments as follows::
39
40 FOO_BAR.add_argument('-x','--xxxx',help='<help text>',[required=True/False])
41
42- Add the action method in the ``core/bar.py`` file, to the Bar class. In effect, the action
43 method would be ``core.bar.Bar.action``. Note that the function name must be same as the
44 action name
45
46- If there is no specific scope, skip the scope parser and add the action the Misc class
47 under ``core/misc.py``. The action would be ``core.misc.Misc.action``
48
49Writing a new command for the Shell
50===================================
51
52To write a new shell command, first add a new parser for the command to the ``client/parsers`` directory.
53
54Add the command method to the ``client/shell.py``
55
56Import the parser in the corresponding method and parse the user input, present in the ``self.line`` attribute
57to obtain the arguments. Perform the action.
058
=== added directory 'src/mailmanclient/cli/lib'
=== added file 'src/mailmanclient/cli/lib/__init__.py'
=== added file 'src/mailmanclient/cli/lib/colors.py'
--- src/mailmanclient/cli/lib/colors.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/lib/colors.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,32 @@
1# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
2#
3# This file is part of mailman.client.
4#
5# mailman.client is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Lesser General Public License as published by the
7# Free Software Foundation, version 3 of the License.
8#
9# mailman.client is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12# License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
16#
17# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
18#
19# Author : Rajeev S <rajeevs1992@gmail.com>
20# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
21# Abhilash Raj <raj.abhilash1@gmail.com>
22# Barry Warsaw <barry@list.org>
23
24GREY = "\033[90m%s\033[0m"
25RED = "\033[91m%s\033[0m"
26GREEN = "\033[92m%s\033[0m"
27YELLOW = "\033[93m%s\033[0m"
28BLUE = "\033[94m%s\033[0m"
29PURPLE = "\033[95m%s\033[0m"
30CYAN = "\033[96m%s\033[0m"
31WHITE = "\033[97m%s\033[0m"
32BLACK = "\033[98m%s\033[0m"
033
=== added file 'src/mailmanclient/cli/lib/mailman_utils.py'
--- src/mailmanclient/cli/lib/mailman_utils.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/lib/mailman_utils.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,117 @@
1#!/usr/bin/python
2
3# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
4#
5# This file is part of mailman.client.
6#
7# mailman.client is free software: you can redistribute it and/or modify it
8# under the terms of the GNU Lesser General Public License as published by the
9# Free Software Foundation, version 3 of the License.
10#
11# mailman.client is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14# License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
18#
19# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
20#
21# Author : Rajeev S <rajeevs1992@gmail.com>
22# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
23# Abhilash Raj <raj.abhilash1@gmail.com>
24# Barry Warsaw <barry@list.org>
25
26from mailmanclient import Client, MailmanConnectionError
27from mailman.config import config
28from mailmanclient.cli.lib.utils import Utils
29
30
31class MailmanUtils(Utils):
32
33 """ Utilities relating to Mailman
34 Client or the REST API
35 """
36
37 def __init__(self):
38 config.load()
39
40 def connect(self, *args, **kwargs):
41 """ Connect to Mailman REST API using the arguments specified.
42 Missing arguments are decided from the mailman.cfg file
43 return a client object.
44 """
45 host, port, username, password = self.get_credentials_from_config()
46
47 if 'host' in kwargs and kwargs['host']:
48 host = kwargs['host']
49 if 'port' in kwargs and kwargs['port']:
50 port = kwargs['port']
51 if 'username' in kwargs and kwargs['username']:
52 username = kwargs['username']
53 if 'password' in kwargs and kwargs['password']:
54 password = kwargs['password']
55
56 client = Client('%s:%s/3.0' % (host, port),
57 username,
58 password)
59 try:
60 client.system
61 except MailmanConnectionError as e:
62 self.error(e)
63 exit(1)
64 return client
65
66 def get_credentials_from_config(self):
67 """ Returns the credentials required for logging on to
68 the Mailman REST API, that are read from the Mailman
69 configuration.
70 """
71 host = 'http://' + config.schema['webservice']['hostname']
72 port = config.schema['webservice']['port']
73 username = config.schema['webservice']['admin_user']
74 password = config.schema['webservice']['admin_pass']
75 return host, port, username, password
76
77 def get_new_domain_name(self):
78 """ Generates the name of a non existent domain """
79 client = self.connect()
80 while True:
81 domain_name = self.get_random_string(10) + '.com'
82 try:
83 client.get_domain(domain_name)
84 continue
85 except Exception:
86 return domain_name
87
88 def add_shell_vars(self, arg, shell):
89 """ Replaces the variables used in the command with thier respective
90 values if the values are present in the shell environment, else
91 use the variable as such.
92 """
93 if not shell.env_on or not arg:
94 return arg
95 if arg[0] == '$' and arg[1:] in shell.env:
96 arg = shell.env[arg[1:]]
97 return arg
98
99 def add_reserved_vars(self, args, shell):
100 """ Adds the reserved variables to a filter query. The reserved variables
101 are domain, list and user, which are added to respective scopes and
102 atrribute names.
103 """
104 scope = args['scope']
105 if 'filters' not in args:
106 args['filters'] = []
107 if not shell.env_on:
108 return args
109 filters = args['filters']
110 if scope == 'list':
111 if 'domain' in shell.env:
112 filters.append(('mail_host', '=', shell.env['domain']))
113 elif scope == 'user':
114 if 'list' in shell.env:
115 filters.append((shell.env['list'], 'in', 'subscriptions'))
116 args['filters'] = filters
117 return args
0118
=== added file 'src/mailmanclient/cli/lib/utils.py'
--- src/mailmanclient/cli/lib/utils.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/lib/utils.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,216 @@
1#!/usr/bin/python
2
3# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
4#
5# This file is part of mailman.client.
6#
7# mailman.client is free software: you can redistribute it and/or modify it
8# under the terms of the GNU Lesser General Public License as published by the
9# Free Software Foundation, version 3 of the License.
10#
11# mailman.client is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14# License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
18#
19# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
20#
21# Author : Rajeev S <rajeevs1992@gmail.com>
22# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
23# Abhilash Raj <raj.abhilash1@gmail.com>
24# Barry Warsaw <barry@list.org>
25
26import re
27import sys
28import csv
29from hashlib import sha1
30from datetime import datetime
31from mailmanclient.cli.lib import colors
32
33
34class Utils():
35 """ General utilities to be used across the CLI """
36
37 WARN = colors.CYAN
38 ERROR = colors.RED
39 CONFIRM = colors.GREEN
40 EMPHASIZE = colors.PURPLE
41
42 # Give colored output on the CLI"
43 def warn(self, message):
44 print(self.WARN % message)
45
46 def error(self, message):
47 sys.stderr.write((self.ERROR + '\n') % message)
48
49 def confirm(self, message):
50 print(self.CONFIRM % message)
51
52 def emphasize(self, message):
53 print(self.EMPHASIZE % message)
54
55 def return_emphasize(self, message):
56 return self.EMPHASIZE % message
57 # End Colors!
58
59 def get_random_string(self, length):
60 """ Returns short random strings, less than 40 bytes in length.
61
62 :param length: Length of the random string to be returned
63 :type length: int
64 """
65 if length > 40:
66 raise Exception('Specify length less than 40')
67 return sha1(str(datetime.now()).encode('utf-8')).hexdigest()[:length]
68
69 def set_table_section_heading(self, table, heading):
70 table.append(['', ''])
71 table.append([heading, ''])
72 table.append(['=============', ''])
73
74 def write_csv(self, table, headers, filename):
75 if table == []:
76 return
77
78 if 'csv' not in filename:
79 filename += '.csv'
80
81 f = open(filename, 'wb')
82 writer = csv.writer(f, quoting=csv.QUOTE_ALL)
83 if headers:
84 writer.writerow(headers)
85 for row in table:
86 writer.writerow(row)
87 f.close()
88
89 return
90
91 def stem(self, arguments):
92 """ Allows the scope to be in singular or plural
93
94 :param arguments: The sys.argv, passed from mmclient
95 :type arguments: list
96 """
97 scope = arguments[2]
98 if scope[-1] == 's':
99 scope = scope[:-1]
100 arguments[2] = scope
101 return arguments
102
103 def get_listing(self, objects, fields):
104 """ Tabulates the set of objects by using the values of each of the
105 fileds. """
106 table = []
107 for obj in objects:
108 row = []
109 for field in fields:
110 try:
111 value = getattr(obj, field)
112 except:
113 value = 'None'
114
115 _type = type(value).__name__
116 if _type == '_Addresses':
117 row.append(str(value[0]))
118 else:
119 row.append(str(value))
120
121 table.append(row)
122 return table
123
124
125class Filter():
126 """ This class manages the filtering tasks for the show and delete commands
127 The class supports three filters, equality, regular expression search
128 and list search.
129 """
130
131 def get_results(self, *args):
132 op = args[2]
133 if op == '=':
134 return self.equality(*args)
135 elif op == 'like':
136 return self.like(*args)
137 elif op == 'in':
138 return self.in_list(*args)
139 else:
140 raise Exception('Invalid operator: %s ' % (op))
141
142 def equality(self, key, value, op, data_set):
143 copy_set = data_set[:]
144 for i in data_set:
145 try:
146 obj_value = getattr(i, key)
147 if obj_value != value:
148 copy_set.remove(i)
149 except AttributeError:
150 raise Exception('Invalid filter : %s' % key)
151 return copy_set
152
153 def in_list(self, key, value, op, data_set):
154 copy_set = data_set[:]
155 flag = False
156 for i in data_set:
157 try:
158 the_list = getattr(i, key)
159 except KeyError:
160 copy_set.remove(i)
161 continue
162 except AttributeError:
163 raise Exception('Invalid filter : %s' % key)
164
165 if key == 'members':
166 for j in the_list:
167 if self.match_pattern(j.email, value):
168 flag = True
169 break
170 elif key == 'lists':
171 for j in the_list:
172 if (self.match_pattern(j.list_id, value)
173 or self.match_pattern(j.fqdn_listname, value)):
174 flag = True
175 break
176 elif key == 'subscriptions':
177 value = value.replace('@', '.')
178 for j in the_list:
179 if self.match_pattern(j.list_id, value):
180 flag = True
181 break
182 else:
183 for j in the_list:
184 if self.match_pattern(j, value):
185 flag = True
186 break
187 if not flag:
188 copy_set.remove(i)
189 flag = False
190 return copy_set
191
192 def like(self, key, value, op, data_set):
193 copy_set = data_set[:]
194 for i in data_set:
195 obj_value = None
196 try:
197 obj_value = getattr(i, key)
198 except KeyError:
199 copy_set.remove(i)
200 except AttributeError:
201 raise Exception('Invalid filter : %s' % key)
202 if not self.match_pattern(obj_value, value):
203 copy_set.remove(i)
204 return copy_set
205
206 def match_pattern(self, string, value):
207 """ Regular expression matcher, returns the match object
208 or None
209 """
210 pattern = None
211 try:
212 pattern = re.compile(value.lower())
213 except:
214 raise Exception('Invalid pattern : %s' % value)
215 string = str(string).lower()
216 return pattern.match(string)
0217
=== added file 'src/mailmanclient/cli/mmclient'
--- src/mailmanclient/cli/mmclient 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/mmclient 2015-03-17 19:08:34 +0000
@@ -0,0 +1,46 @@
1#!/usr/bin/python
2
3# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
4#
5# This file is part of mailman.client.
6#
7# mailman.client is free software: you can redistribute it and/or modify it
8# under the terms of the GNU Lesser General Public License as published by the
9# Free Software Foundation, version 3 of the License.
10#
11# mailman.client is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14# License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
18#
19# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
20#
21# Author : Rajeev S <rajeevs1992@gmail.com>
22# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
23# Abhilash Raj <raj.abhilash1@gmail.com>
24# Barry Warsaw <barry@list.org>
25
26import sys
27from mailmanclient.cli.lib.utils import Utils
28from mailmanclient.cli.client.shell import Shell
29from mailmanclient.cli.client.cmdparser import CmdParser
30try:
31 # If the mmclient is invoked with arguments, trigger the command
32 # line tools, else trigger the commandline shell
33 sys.argv[1]
34 utils = Utils()
35 arguments = None
36 try:
37 arguments = utils.stem(sys.argv)
38 except IndexError:
39 pass
40 c = CmdParser(arguments)
41 c.run()
42except IndexError:
43 # No arguments supplied.Start the shell.
44 s = Shell()
45 s.initialize()
46 s.cmdloop()
047
=== added directory 'src/mailmanclient/cli/tests'
=== added file 'src/mailmanclient/cli/tests/__init__.py'
=== added file 'src/mailmanclient/cli/tests/test_domain.py'
--- src/mailmanclient/cli/tests/test_domain.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/tests/test_domain.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,223 @@
1#!/usr/bin/python
2
3# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
4#
5# This file is part of mailman.client.
6#
7# mailman.client is free software: you can redistribute it and/or modify it
8# under the terms of the GNU Lesser General Public License as published by the
9# Free Software Foundation, version 3 of the License.
10#
11# mailman.client is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14# License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
18#
19# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
20#
21# Author : Rajeev S <rajeevs1992@gmail.com>
22# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
23# Abhilash Raj <raj.abhilash1@gmail.com>
24# Barry Warsaw <barry@list.org>
25
26import random
27import unittest
28from mock import patch
29from six.moves.urllib_error import HTTPError
30from io import StringIO
31from mailmanclient.cli.core.domains import Domains, DomainException
32from mailmanclient.cli.lib.mailman_utils import MailmanUtils
33
34
35class TestCreateDomain(unittest.TestCase):
36
37 domain_names = []
38 utils = MailmanUtils()
39
40 def setUp(self):
41 self.client = self.utils.connect()
42 self.domain = Domains(self.client)
43 self.test_domain = self.utils.get_new_domain_name()
44 self.domain_names.append(self.test_domain)
45 self.client.create_domain(self.test_domain)
46
47 def test_normal_create(self):
48 self.new_domain = self.utils.get_new_domain_name()
49 self.domain_names.append(self.new_domain)
50 args = {}
51 args['domain'] = self.new_domain
52 args['contact'] = 'a@b.com'
53 self.domain.create(args)
54
55 def test_create_existent_domain(self):
56 args = {}
57 args['domain'] = self.test_domain
58 args['contact'] = 'a@b.com'
59 self.assertRaises(DomainException, self.domain.create, args)
60
61 def test_no_postmaster(self):
62 self.new_domain = self.utils.get_new_domain_name()
63 self.domain_names.append(self.new_domain)
64 args = {}
65 args['domain'] = self.new_domain
66 args['contact'] = None
67 self.domain.create(args)
68
69 def tearDown(self):
70 for domain in self.domain_names:
71 try:
72 self.client.delete_domain(domain)
73 except Exception:
74 pass
75
76
77class TestShowDomain(unittest.TestCase):
78
79 domain_names = []
80 utils = MailmanUtils()
81
82 def setUp(self):
83 self.client = self.utils.connect()
84 self.domain = Domains(self.client)
85
86 @patch('sys.stdout', new_callable=StringIO)
87 def test_normal_show(self, output):
88 ndomains = len(self.client.domains)
89 args = {}
90 args['no_header'] = False
91 args['verbose'] = False
92 args['csv'] = None
93 args['domain'] = None
94 self.domain.show(args)
95 domain_list = output.getvalue().split('\n')
96 count = len(domain_list) - 1
97 self.assertEqual(ndomains, count)
98
99 @patch('sys.stdout', new_callable=StringIO)
100 def test_verbose_show(self, output):
101 args = {}
102 args['no_header'] = False
103 args['verbose'] = True
104 args['csv'] = None
105 args['domain'] = None
106 test_domain = random.randint(0, len(self.client.domains) - 1)
107 test_domain = self.client.domains[test_domain]
108 self.domain.show(args)
109 domain_list = output.getvalue().split('\n')
110 domain = ''
111 for domain in domain_list:
112 if test_domain.base_url in domain:
113 break
114 domain = domain.split()
115 cleaned_domain = []
116 for attribute in domain:
117 if attribute:
118 cleaned_domain.append(attribute)
119 self.assertEqual(cleaned_domain[0], test_domain.base_url)
120 self.assertEqual(cleaned_domain[1], test_domain.contact_address)
121 self.assertEqual(cleaned_domain[2], test_domain.mail_host)
122 self.assertEqual(cleaned_domain[3], test_domain.url_host)
123
124 @patch('sys.stdout', new_callable=StringIO)
125 def test_no_header(self, output):
126 args = {}
127 args['no_header'] = True
128 args['csv'] = None
129 args['verbose'] = True
130 args['domain'] = None
131 test_domain = random.randint(0, len(self.client.domains) - 1)
132 test_domain = self.client.domains[test_domain]
133 self.domain.show(args)
134 domain_list = output.getvalue().split('\n')
135 line_one = domain_list[0].split()
136 self.assertNotEqual(line_one[0], 'Base')
137 domain = ''
138 for domain in domain_list:
139 if test_domain.base_url in domain:
140 break
141 domain = domain.split()
142 cleaned_domain = []
143 for attribute in domain:
144 if attribute:
145 cleaned_domain.append(attribute)
146 self.assertEqual(cleaned_domain[0], test_domain.base_url)
147 self.assertEqual(cleaned_domain[1], test_domain.contact_address)
148 self.assertEqual(cleaned_domain[2], test_domain.mail_host)
149 self.assertEqual(cleaned_domain[3], test_domain.url_host)
150
151 def tearDown(self):
152 for domain in self.domain_names:
153 try:
154 self.client.delete_domain(domain)
155 except Exception:
156 pass
157
158
159class TestDeleteDomain(unittest.TestCase):
160
161 domain_names = []
162 utils = MailmanUtils()
163
164 def setUp(self):
165 self.client = self.utils.connect()
166 self.domain = Domains(self.client)
167
168 def test_normal_delete(self):
169 new_domain = self.utils.get_new_domain_name()
170 self.domain_names.append(new_domain)
171 self.client.create_domain(new_domain)
172 args = {}
173 args['domain'] = new_domain
174 with patch('builtins.input', return_value='y'):
175 args['yes'] = True
176 self.domain.delete(args)
177 self.assertRaises(HTTPError, self.client.get_domain, new_domain)
178
179 def test_delete_cancel(self):
180 new_domain = self.utils.get_new_domain_name()
181 self.domain_names.append(new_domain)
182 self.client.create_domain(new_domain)
183 args = {}
184 args['domain'] = new_domain
185 with patch('builtins.input', return_value='n'):
186 args['yes'] = False
187 self.domain.delete(args)
188 self.assertRaises(HTTPError, self.client.create_domain, new_domain)
189
190 def test_delete_invalid_confirm(self):
191 new_domain = self.utils.get_new_domain_name()
192 self.domain_names.append(new_domain)
193 self.client.create_domain(new_domain)
194 args = {}
195 args['domain'] = new_domain
196 args['yes'] = False
197 with patch('builtins.input', return_value='no'):
198 self.assertRaises(Exception, self.domain.delete, args)
199
200 def test_delete_without_confirm(self):
201 new_domain = self.utils.get_new_domain_name()
202 self.domain_names.append(new_domain)
203 self.client.create_domain(new_domain)
204 args = {}
205 args['domain'] = new_domain
206 args['yes'] = True
207 self.domain.delete(args)
208 self.assertRaises(HTTPError, self.client.get_domain, new_domain)
209
210 def test_delete_invalid_domain(self):
211 new_domain = self.utils.get_new_domain_name()
212 self.domain_names.append(new_domain)
213 args = {}
214 args['domain'] = new_domain
215 args['yes'] = True
216 self.assertRaises(DomainException, self.domain.delete, args)
217
218 def tearDown(self):
219 for domain in self.domain_names:
220 try:
221 self.client.delete_domain(domain)
222 except Exception:
223 pass
0224
=== added file 'src/mailmanclient/cli/tests/test_list.py'
--- src/mailmanclient/cli/tests/test_list.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/tests/test_list.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,285 @@
1#!/usr/bin/python
2
3# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
4#
5# This file is part of mailman.client.
6#
7# mailman.client is free software: you can redistribute it and/or modify it
8# under the terms of the GNU Lesser General Public License as published by the
9# Free Software Foundation, version 3 of the License.
10#
11# mailman.client is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14# License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
18#
19# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
20#
21# Author : Rajeev S <rajeevs1992@gmail.com>
22# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
23# Abhilash Raj <raj.abhilash1@gmail.com>
24# Barry Warsaw <barry@list.org>
25
26import unittest
27from mock import patch
28from six.moves.urllib_error import HTTPError
29from io import StringIO
30from mailmanclient.cli.core.lists import Lists, ListException
31from mailmanclient.cli.core.domains import DomainException
32from mailmanclient.cli.lib.mailman_utils import MailmanUtils
33
34
35class TestCreateList(unittest.TestCase):
36
37 domain_names = []
38 utils = MailmanUtils()
39
40 def setUp(self):
41 self.client = self.utils.connect()
42 self._list = Lists(self.client)
43 self.test_domain = self.utils.get_new_domain_name()
44 self.domain_names.append(self.test_domain)
45 self.client.create_domain(self.test_domain)
46
47 # Raise exception if domain create falied.
48 self.domain = self.client.get_domain(self.test_domain)
49
50 def test_normal_create(self):
51 args = {}
52 list_name = self.utils.get_random_string(5)
53 list_name += '@' + self.test_domain
54 args['list'] = list_name
55 self._list.create(args)
56
57 def test_create_existent_list(self):
58 list_name = self.utils.get_random_string(5)
59 self.domain.create_list(list_name)
60 args = {}
61 list_name += '@' + self.test_domain
62 args['list'] = list_name
63 self.assertRaises(ListException, self._list.create, args)
64
65 def test_invalid_fqdn_create(self):
66 test_list = self.utils.get_random_string(5)
67 invalid_name_1 = test_list + '@'
68 invalid_name_2 = '@' + self.test_domain
69 invalid_name_3 = (test_list + '@' +
70 self.utils.get_new_domain_name())
71 invalid_name_4 = self.test_domain
72 args = {}
73 args['list'] = invalid_name_1
74 self.assertRaises(ListException, self._list.create, args)
75 args['list'] = invalid_name_2
76 self.assertRaises(ListException, self._list.create, args)
77 args['list'] = invalid_name_3
78 self.assertRaises(DomainException, self._list.create, args)
79 args['list'] = invalid_name_4
80 self.assertRaises(ListException, self._list.create, args)
81
82 def tearDown(self):
83 for domain in self.domain_names:
84 try:
85 self.client.delete_domain(domain)
86 except Exception:
87 pass
88
89
90class TestShowList(unittest.TestCase):
91
92 domain_names = []
93 utils = MailmanUtils()
94
95 def setUp(self):
96 self.client = self.utils.connect()
97 self._list = Lists(self.client)
98 self.test_domain = self.utils.get_new_domain_name()
99 self.test_list = self.utils.get_random_string(5)
100 self.domain_names.append(self.test_domain)
101 self.client.create_domain(self.test_domain)
102
103 # Raise exception if domain create falied.
104 self.domain = self.client.get_domain(self.test_domain)
105 self.domain.create_list(self.test_list)
106
107 @patch('sys.stdout', new_callable=StringIO)
108 def test_normal_show(self, output):
109 nlists = len(self.client.lists)
110 args = {}
111 args['no_header'] = False
112 args['csv'] = None
113 args['verbose'] = False
114 args['domain'] = None
115 args['list'] = None
116 self._list.show(args)
117 mlist_list = output.getvalue().split('\n')
118 count = len(mlist_list) - 1
119 self.assertEqual(nlists, count)
120
121 @patch('sys.stdout', new_callable=StringIO)
122 def test_filter_show(self, output):
123 nlists = len(self.domain.lists)
124 args = {}
125 args['no_header'] = False
126 args['csv'] = None
127 args['verbose'] = False
128 args['domain'] = self.test_domain
129 args['list'] = None
130 self._list.show(args)
131 mlist_list = output.getvalue().split('\n')
132 count = len(mlist_list) - 1
133 self.assertEqual(nlists, count)
134
135 @patch('sys.stdout', new_callable=StringIO)
136 def test_verbose_show(self, output):
137 args = {}
138 args['no_header'] = False
139 args['csv'] = None
140 args['verbose'] = True
141 args['domain'] = None
142 args['list'] = None
143 self._list.show(args)
144 mlists = output.getvalue().split('\n')
145 test_list = '%s@%s' % (self.test_list, self.test_domain)
146 mlist = ''
147 for mlist in mlists:
148 if test_list in mlist:
149 break
150 mlist = mlist.split()
151 cleaned_list = []
152 for attribute in mlist:
153 if attribute:
154 cleaned_list.append(attribute)
155 mlist = self.client.get_list(test_list)
156 self.assertEqual(cleaned_list[0], mlist.list_id)
157 self.assertEqual(cleaned_list[1], mlist.list_name)
158 self.assertEqual(cleaned_list[2], mlist.mail_host)
159 self.assertEqual(cleaned_list[3], mlist.display_name)
160 self.assertEqual(cleaned_list[4], mlist.fqdn_listname)
161
162 @patch('sys.stdout', new_callable=StringIO)
163 def test_filter_verbose_show(self, output):
164 args = {}
165 args['no_header'] = False
166 args['csv'] = None
167 args['verbose'] = True
168 args['domain'] = self.test_domain
169 args['list'] = None
170 self._list.show(args)
171 mlists = output.getvalue().split('\n')
172 mlist = ''
173 test_list = '%s@%s' % (self.test_list, self.test_domain)
174 for mlist in mlists:
175 if test_list in mlist:
176 break
177 mlist = mlist.split()
178 cleaned_list = []
179 for attribute in mlist:
180 if attribute:
181 cleaned_list.append(attribute)
182 mlist = self.client.get_list(test_list)
183 self.assertEqual(cleaned_list[0], mlist.list_id)
184 self.assertEqual(cleaned_list[1], mlist.list_name)
185 self.assertEqual(cleaned_list[2], mlist.mail_host)
186 self.assertEqual(cleaned_list[3], mlist.display_name)
187 self.assertEqual(cleaned_list[4], mlist.fqdn_listname)
188
189 @patch('sys.stdout', new_callable=StringIO)
190 def test_no_header(self, output):
191 args = {}
192 args['no_header'] = False
193 args['csv'] = None
194 args['verbose'] = True
195 args['domain'] = None
196 args['list'] = None
197 self._list.show(args)
198 mlists = output.getvalue().split('\n')
199 line_one = mlists[0].split()
200 self.assertNotEqual(line_one[0], 'Base')
201 mlist = ''
202 for mlist in mlists:
203 if self.test_list in mlist:
204 break
205 mlist = mlist.split()
206 cleaned_list = []
207 for attribute in mlist:
208 if attribute:
209 cleaned_list.append(attribute)
210 mlist = self.client.get_list('%s@%s' % (self.test_list,
211 self.test_domain))
212 self.assertEqual(cleaned_list[0], mlist.list_id)
213 self.assertEqual(cleaned_list[1], mlist.list_name)
214 self.assertEqual(cleaned_list[2], mlist.mail_host)
215 self.assertEqual(cleaned_list[3], mlist.display_name)
216 self.assertEqual(cleaned_list[4], mlist.fqdn_listname)
217
218 def tearDown(self):
219 for domain in self.domain_names:
220 try:
221 self.client.delete_domain(domain)
222 except Exception:
223 pass
224
225
226class TestDeleteList(unittest.TestCase):
227
228 domain_names = []
229 utils = MailmanUtils()
230
231 def setUp(self):
232 self.client = self.utils.connect()
233 self._list = Lists(self.client)
234 self.test_domain = self.utils.get_new_domain_name()
235 self.test_list = self.utils.get_random_string(5)
236 self.domain_names.append(self.test_domain)
237 self.client.create_domain(self.test_domain)
238
239 # Raise exception if domain create falied.
240 self.domain = self.client.get_domain(self.test_domain)
241 self.domain.create_list(self.test_list)
242
243 def test_normal_delete(self):
244 new_list = self.utils.get_random_string(5)
245 self.domain.create_list(new_list)
246 args = {}
247 args['list'] = '%s@%s' % (new_list, self.test_domain)
248 with patch('builtins.input', return_value='y'):
249 args['yes'] = True
250 self._list.delete(args)
251 self.assertRaises(HTTPError, self.client.get_list, args['list'])
252
253 def test_delete_cancel(self):
254 new_list = self.utils.get_random_string(5)
255 self.domain.create_list(new_list)
256 args = {}
257 args['list'] = '%s@%s' % (new_list, self.test_domain)
258 with patch('builtins.input', return_value='n'):
259 args['yes'] = False
260 self._list.delete(args)
261 self.assertRaises(HTTPError, self.domain.create_list, args['list'])
262
263 def test_delete_invalid_confirm(self):
264 new_list = self.utils.get_random_string(5)
265 self.domain.create_list(new_list)
266 args = {}
267 args['list'] = '%s@%s' % (new_list, self.test_domain)
268 with patch('builtins.input', return_value='no'):
269 self.assertRaises(Exception, self._list.delete, args)
270
271 def test_delete_without_confirm(self):
272 new_list = self.utils.get_random_string(5)
273 self.domain.create_list(new_list)
274 args = {}
275 args['list'] = '%s@%s' % (new_list, self.test_domain)
276 args['yes'] = True
277 self._list.delete(args)
278 self.assertRaises(HTTPError, self.client.get_list, args['list'])
279
280 def tearDown(self):
281 for domain in self.domain_names:
282 try:
283 self.client.delete_domain(domain)
284 except Exception:
285 pass
0286
=== added file 'src/mailmanclient/cli/tests/test_preference.py'
--- src/mailmanclient/cli/tests/test_preference.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/tests/test_preference.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,99 @@
1#!/usr/bin/python
2
3# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
4#
5# This file is part of mailman.client.
6#
7# mailman.client is free software: you can redistribute it and/or modify it
8# under the terms of the GNU Lesser General Public License as published by the
9# Free Software Foundation, version 3 of the License.
10#
11# mailman.client is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14# License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
18#
19# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
20#
21# Author : Rajeev S <rajeevs1992@gmail.com>
22# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
23# Abhilash Raj <raj.abhilash1@gmail.com>
24# Barry Warsaw <barry@list.org>
25
26import unittest
27from mailmanclient.cli.core.preferences import Preferences
28from mailmanclient.cli.lib.mailman_utils import MailmanUtils
29
30
31utils = MailmanUtils()
32
33
34class TestUpdate(unittest.TestCase):
35
36 new_objects = []
37
38 test_user = None
39 test_domain = None
40 test_list = None
41 test_address = None
42 test_member = None
43
44 def setUp(self):
45 self.client = utils.connect()
46 self.preferences = Preferences(self.client)
47 self.test_user = 'a@' + utils.get_random_string(5) + '.com'
48 self.test_user = self.client.create_user(self.test_user,
49 display_name='a',
50 password='a')
51 self.new_objects.append(self.test_user)
52
53 self.test_domain = self.client.create_domain(utils.get_random_string(5)
54 + '.org')
55 self.new_objects.append(self.test_domain)
56 self.test_list = self.test_domain.create_list(utils.get_random_string(5))
57 self.new_objects.append(self.test_list)
58
59 self.test_member = self.test_list.subscribe(self.test_user.addresses[0])
60
61 self.test_address = self.test_user.addresses[0]
62
63 def test_user(self):
64 args = {}
65 args['update_scope'] = 'user'
66 args['email'] = self.test_user.addresses[0]
67 args['key'] = 'receive_list_copy'
68 args['value'] = 'False'
69 self.preferences.update(args)
70 self.assertFalse(self.test_user.preferences['receive_list_copy'])
71
72 def test_address(self):
73 args = {}
74 args['update_scope'] = 'address'
75 args['email'] = self.test_address
76 args['key'] = 'receive_list_copy'
77 args['value'] = 'False'
78 self.preferences.update(args)
79 self.assertFalse(self.test_address.preferences['receive_list_copy'])
80
81 def test_member(self):
82 args = {}
83 args['update_scope'] = 'member'
84 args['email'] = self.test_member.email
85 args['list'] = self.test_list.fqdn_listname
86 args['key'] = 'receive_list_copy'
87 args['value'] = 'False'
88 self.preferences.update(args)
89 self.assertFalse(self.test_member.preferences['receive_list_copy'])
90
91 def tearDown(self):
92 for obj in self.new_objects:
93 try:
94 if type(obj).__name__ == '_Domain':
95 self.client.delete_domain(obj.base_url)
96 else:
97 obj.delete()
98 except:
99 pass
0100
=== added file 'src/mailmanclient/cli/tests/test_user.py'
--- src/mailmanclient/cli/tests/test_user.py 1970-01-01 00:00:00 +0000
+++ src/mailmanclient/cli/tests/test_user.py 2015-03-17 19:08:34 +0000
@@ -0,0 +1,127 @@
1#!/usr/bin/python
2
3# Copyright (C) 2010-2014 by the Free Software Foundation, Inc.
4#
5# This file is part of mailman.client.
6#
7# mailman.client is free software: you can redistribute it and/or modify it
8# under the terms of the GNU Lesser General Public License as published by the
9# Free Software Foundation, version 3 of the License.
10#
11# mailman.client is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14# License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public License
17# along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
18#
19# This file is part of the Mailman CLI Project, Google Summer Of Code, 2014
20#
21# Author : Rajeev S <rajeevs1992@gmail.com>
22# Mentors : Stephen J. Turnbull <stephen@xemacs.org>
23# Abhilash Raj <raj.abhilash1@gmail.com>
24# Barry Warsaw <barry@list.org>
25
26import unittest
27from mailmanclient.cli.core.users import Users, UserException
28from mailmanclient.cli.lib.mailman_utils import MailmanUtils
29
30
31class TestCreateUser(unittest.TestCase):
32
33 utils = MailmanUtils()
34 new_users = []
35
36 def setUp(self):
37 self.client = self.utils.connect()
38 self.users = Users(self.client)
39 self.test_user = 'a@' + self.utils.get_random_string(5) + '.org'
40 self.new_users.append(self.test_user)
41 self.client.create_user(email=self.test_user,
42 password='abcdefgh',
43 display_name='abc')
44 self.test_list = self.client.lists[0]
45 self.test_list.subscribe(self.test_user)
46
47 # Test if user created else setup fails.
48 self.client.get_user(self.test_user)
49
50 def test_normal_create(self):
51 args = {}
52 new_user = 'a@' + self.utils.get_random_string(5) + '.org'
53 self.new_users.append(new_user)
54 args['email'] = new_user
55 args['password'] = 'abc'
56 args['name'] = 'abc'
57 self.users.create(args)
58 self.assertRaises(UserException, self.users.create, args)
59
60 def test_create_existent_user(self):
61 args = {}
62 args['email'] = self.test_user
63 args['password'] = 'abc'
64 args['name'] = 'abc'
65 self.assertRaises(UserException, self.users.create, args)
66
67 def tearDown(self):
68 for user in self.new_users:
69 try:
70 u = self.client.get_user(user)
71 u.delete()
72 except Exception as e:
73 print(e)
74
75
76class TestSubscription(unittest.TestCase):
77
78 utils = MailmanUtils()
79 new_users = []
80 domain_list = []
81
82 def setUp(self):
83 self.client = self.utils.connect()
84 self.users = Users(self.client)
85
86 self.test_domain = self.utils.get_new_domain_name()
87 self.domain_list.append(self.test_domain)
88 self.client.create_domain(self.test_domain)
89
90 self.test_user = self.utils.get_random_string(8) + '@domain.org'
91 self.new_users.append(self.test_user)
92 self.client.create_user(email=self.test_user,
93 password='abcdefgh',
94 display_name='abc')
95 self.client.get_user(self.test_user)
96 domain = self.client.get_domain(self.test_domain)
97 self.test_list = (self.utils.get_random_string(8) +
98 '@' + self.test_domain)
99 domain.create_list(self.test_list.split('@')[0])
100
101 # Test if user created else setup fails.
102 self.client.get_user(self.test_user)
103
104 def test_subscribe_to_list(self):
105 print(self.test_user)
106 _list = self.client.get_list(self.test_list)
107 nmembers = len(_list.members)
108 args = {}
109 args['users'] = ['a2b@b.com', 'ab4@c.com', 'a8d@e.com']
110 self.new_users.extend(args['users'])
111 args['list_name'] = self.test_list
112 args['quiet'] = False
113 self.users.subscribe(args)
114 self.assertEqual(nmembers + 3, len(_list.members))
115
116 def tearDown(self):
117 for i in self.new_users:
118 try:
119 user = self.client.get_user(i)
120 user.delete()
121 except Exception:
122 pass
123 for i in self.domain_list:
124 try:
125 self.client.delete_domain(i)
126 except Exception:
127 pass

Subscribers

People subscribed via source and target branches