├── .gitignore
├── LICENSE
├── README.md
├── SETTINGS.py
├── __init__.py
├── beehive.py
├── hive.db
├── lib
├── __init__.py
├── banners.py
├── beecoroutine.py
├── beethread.py
├── cmd.py
├── cmd2.py
├── db.py
├── exception.py
├── io.py
├── poc.py
├── pprint2.py
└── scanner.py
├── menu.py
├── pocdb.json
├── pocs
└── __init__.py
├── requirements.txt
├── setup.py
└── tmp
└── tmp.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | # updated db
2 | hive.db
3 |
4 | # Byte-compiled / optimized / DLL files
5 | __pycache__/
6 | *.py[cod]
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | .eggs/
14 | *.egg-info/
15 | .installed.cfg
16 | *.egg
17 |
18 | # PyInstaller
19 | # Usually these files are written by a python script from a template
20 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
21 | *.manifest
22 | *.spec
23 |
24 | # Installer logs
25 | pip-log.txt
26 | pip-delete-this-directory.txt
27 |
28 | # Unit test / coverage reports
29 | .tox/
30 | .coverage
31 | .coverage.*
32 | .cache
33 | nosetests.xml
34 | coverage.xml
35 | *,cover
36 |
37 | # Translations
38 | *.mo
39 | *.pot
40 |
41 | # Sphinx documentation
42 | docs/_build/
43 |
44 | # Beehive
45 | .DS_Store
46 | hive.db
47 | pocs/
48 | tmp/
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
676 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Beehive
2 |
3 | Beehive 是一款基于 Beebeeto-framework 的开源安全漏洞检测利用框架,安全研究人员可以通过使用它快速的进行漏洞挖掘、漏洞利用、后续渗透等工作。
4 |
5 | Beehive is an open-source vulnerability detection framework based on Beebeeto-framework. Security researcher can use it to find vulnerability, exploits, subsequent attacks, etc.
6 |
7 |
8 | # Screenshots
9 |
10 | 
11 |
12 | # Documents
13 |
14 | - [http://docs.beebeeto.com/beehive/index.html](http://docs.beebeeto.com/beehive/index.html)
15 |
16 | # Developers
17 |
18 | - all@beebeeto.com
19 | - win2000@unknown.com
20 | - evi1m0.bat@gmail.com
--------------------------------------------------------------------------------
/SETTINGS.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 |
5 | BASE_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__),
6 | '..',
7 | '..',))
8 |
9 | BEESCAN_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__),
10 | '..',))
11 |
12 | FRAMEWORK_DIR = os.path.abspath(os.path.join(BASE_DIR,
13 | 'Beebeeto-framework',))
14 |
15 | POC_DIR = os.path.abspath(os.path.join(BEESCAN_DIR,
16 | 'pocs',))
17 |
18 |
19 | sys.path.extend([BASE_DIR, FRAMEWORK_DIR, POC_DIR])
20 |
21 | VERSION = '0.1.0'
22 |
23 |
24 | if __name__ == '__main__':
25 | print BASE_DIR
26 | print BEESCAN_DIR
27 | print FRAMEWORK_DIR
28 | print POC_DIR
29 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n0tr00t/Beehive/24227d00f2db5299ffd426bc662deac8469badf3/__init__.py
--------------------------------------------------------------------------------
/beehive.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # site : www.beebeeto.com
4 | # team : n0tr00t security
5 |
6 | import sys
7 | import menu
8 | import SETTINGS
9 |
10 | from lib.exception import *
11 |
12 | def checkPythonVersion(major=2, minorLowest=6):
13 | if sys.version_info.major != major or \
14 | sys.version_info.minor < minorLowest:
15 | errInfo = 'your python version is too low, version>=%d.%d.x '\
16 | 'is needed.' % (major, minorLowest)
17 | raise BeeScanLaunchException(errInfo)
18 | return True
19 |
20 | if __name__ == '__main__':
21 | mm = menu.MainMenu()
22 | mm.cmdloop()
23 |
--------------------------------------------------------------------------------
/hive.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n0tr00t/Beehive/24227d00f2db5299ffd426bc662deac8469badf3/hive.db
--------------------------------------------------------------------------------
/lib/__init__.py:
--------------------------------------------------------------------------------
1 | from SETTINGS import *
--------------------------------------------------------------------------------
/lib/banners.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding : utf-8
3 | # author : evi1m0
4 |
5 | import random
6 | import textwrap
7 |
8 | one = textwrap.dedent(
9 | r'''
10 | _______ __ __ __
11 | | \ | \ | \| \
12 | | $$$$$$$\ ______ ______ | $$ | $$ \$$ __ __ ______
13 | | $$__/ $$ / \ / \ | $$__| $$| \| \ / \ / \
14 | | $$ $$| $$$$$$\| $$$$$$\| $$ $$| $$ \$$\ / $$| $$$$$$\
15 | | $$$$$$$\| $$ $$| $$ $$| $$$$$$$$| $$ \$$\ $$ | $$ $$
16 | | $$__/ $$| $$$$$$$$| $$$$$$$$| $$ | $$| $$ \$$ $$ | $$$$$$$$
17 | | $$ $$ \$$ \ \$$ \| $$ | $$| $$ \$$$ \$$ \
18 | \$$$$$$$ \$$$$$$$ \$$$$$$$ \$$ \$$ \$$ \$ \$$$$$$$
19 | ''')
20 |
21 | two = textwrap.dedent(
22 | r'''
23 | ____ U _____ uU _____ u _ _ __ __ U _____ u
24 | U | __")u\| ___"|/\| ___"|/ |'| |'| ___ \ \ /"/u\| ___"|/
25 | \| _ \/ | _|" | _|" /| |_| |\ |_"_| \ \ / // | _|"
26 | | |_) | | |___ | |___ U| _ |u | | /\ V /_,-.| |___
27 | |____/ |_____| |_____| |_| |_| U/| |\u U \_/-(_/ |_____|
28 | _|| \\_ << >> << >> // \\.-,_|___|_,-.// << >>
29 | (__) (__)(__) (__)(__) (__)(_") ("_)\_)-' '-(_/(__) (__) (__)
30 | ''')
31 |
32 | three = textwrap.dedent(
33 | r'''
34 | ,; ,; ,;
35 | . f#i f#i . . t f#i
36 | Ef. .E#t .E#t Di Dt Ej .E#t
37 | E#Wi i#W, i#W, E#i E#i E#, t .DD. i#W,
38 | E#K#D: L#D. L#D. E#t E#t E#t EK: ,WK. L#D.
39 | E#t,E#f. :K#Wfff; :K#Wfff; E#t E#t E#t E#t i#D :K#Wfff;
40 | E#WEE##Wt i##WLLLLt i##WLLLLt E########f. E#t E#t j#f i##WLLLLt
41 | E##Ei;;;;. .E#L .E#L E#j..K#j... E#t E#tL#i .E#L
42 | E#DWWt f#E: f#E: E#t E#t E#t E#WW, f#E:
43 | E#t f#K; ,WW; ,WW; E#t E#t E#t E#K: ,WW;
44 | E#Dfff##E, .D#; .D#; f#t f#t E#t ED. .D#;
45 | jLLLLLLLLL; tt tt ii ii E#t t tt
46 | ''')
47 |
48 | four = textwrap.dedent(
49 | r'''
50 | ___ _ _ _
51 | | _ ) ___ ___ | || | (_) __ __ ___
52 | | _ \ / -_) / -_) | __ | | | \ V / / -_)
53 | |___/ \___| \___| |_||_| _|_|_ _\_/_ \___|
54 | _|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|
55 | "`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
56 | ''')
57 |
58 | def getBanner():
59 | logo = [one, two, three, four]
60 | statistics = random.choice(logo)
61 | return statistics
62 |
--------------------------------------------------------------------------------
/lib/beecoroutine.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # author: windows2000
4 | # site : beebeeto.com
5 | # team : n0tr00t security
6 |
7 | import gevent.monkey
8 |
9 | from copy import deepcopy
10 | from gevent.pool import Pool
11 |
12 | gevent.monkey.patch_all()
13 |
14 |
15 |
16 | class WorkerPool(object):
17 | JOB_UNSTART = 0 # poc not run
18 | JOB_RUNNING = 1
19 | JOB_FINISHED = 2 # poc run ok
20 | JOB_ERROR = -1 # error encountered when run poc
21 | JOB_ABORT = -2 # running poc is abort, viz unfinished
22 |
23 | def __init__(self, concurrency=10):
24 | self.concurrency = concurrency
25 | self.jobPool = Pool(size=concurrency)
26 | self.errNum = 0 # failed job(run time error but not aborted)
27 | self.successNum = 0
28 | self.totalNum = 0
29 | self.results = {}
30 |
31 | def work(self, iterJobFuncArgs, jobFunc, timeout=None):
32 | for jobFuncArgs in iterJobFuncArgs:
33 | self.results[hash(str(jobFuncArgs))] = {
34 | 'state': self.JOB_UNSTART,
35 | 'args': jobFuncArgs,
36 | }
37 | self.totalNum += 1
38 | self.jobPool.add(
39 | self.jobPool.apply_async(
40 | self._doJob,
41 | args=(jobFunc, jobFuncArgs,),
42 | kwds=None,
43 | callback=self._cbJobFinished
44 | )
45 | )
46 | self.jobPool.join(timeout=timeout, raise_error=False)
47 | return self.results
48 |
49 | def _cbJobFinished(self, jobResult):
50 | if jobResult['state'] == self.JOB_ERROR:
51 | self.errNum += 1
52 | elif jobResult['state'] == self.JOB_FINISHED:
53 | self.successNum += 1
54 |
55 | def _doJob(self, jobFunc, jobFuncArgs):
56 | self.results[hash(str(jobFuncArgs))]['state'] = self.JOB_RUNNING
57 | try:
58 | self.results[hash(str(jobFuncArgs))]['jobRet'] = \
59 | jobFunc(*jobFuncArgs) if isinstance(jobFuncArgs, list) \
60 | else jobFunc(jobFuncArgs)
61 | self.results[hash(str(jobFuncArgs))]['state'] = self.JOB_FINISHED
62 | except Exception as err:
63 | self.results[hash(str(jobFuncArgs))]['exception'] = str(err)
64 | self.results[hash(str(jobFuncArgs))]['state'] = self.JOB_ERROR
65 | return self.results[hash(str(jobFuncArgs))]
66 |
67 | def handleAbort(self):
68 | for jobId in self.results.keys():
69 | if self.results[jobId]['state'] in (self.JOB_RUNNING,
70 | self.JOB_UNSTART):
71 | self.results[jobId]['state'] = self.JOB_ABORT
72 |
73 |
74 | if __name__ == '__main__':
75 | # testing code
76 | import time
77 | from random import randint
78 | from pprint import pprint
79 |
80 | def test(index, sleepTime):
81 | time.sleep(sleepTime)
82 | if sleepTime % 3 == 0:
83 | raise ValueError("%d - %d : 3's multiple!" % (index,
84 | sleepTime))
85 | else:
86 | return True
87 |
88 | try:
89 | wp = WorkerPool(concurrency=10)
90 | wp.work([(i, randint(1, 3)) for i in xrange(20)], test, timeout=None)
91 | pprint(wp.results)
92 | except KeyboardInterrupt, SystemExit:
93 | wp.handleAbort()
94 | pprint(wp.results)
95 | exit()
96 | finally:
97 | print 'errNum %d, successNum %d, totalNum %d' % \
98 | (wp.errNum, wp.successNum, wp.totalNum)
99 |
--------------------------------------------------------------------------------
/lib/beethread.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # author: windows2000
4 | # site : beebeeto.com
5 | # team : n0tr00t security
6 |
7 |
8 | import threadpool as tp
9 |
10 | from copy import deepcopy
11 |
12 |
13 | class WorkerPool(object):
14 | JOB_UNSTART = 0 # poc not run
15 | JOB_RUNNING = 1
16 | JOB_FINISHED = 2 # poc run ok
17 | JOB_ERROR = -1 # error encountered when run poc
18 | JOB_ABORT = -2 # running poc is abort, viz unfinished
19 |
20 | def __init__(self, concurrency=10):
21 | self.concurrency = concurrency
22 | self.jobPool = tp.ThreadPool(num_workers=concurrency)
23 | self.errNum = 0
24 | self.successNum = 0
25 | self.totalNum = 0
26 | self.results = {}
27 |
28 | def work(self, iterJobFuncArgs, jobFunc, timeout=None):
29 | def argsGenerator():
30 | for jobFuncArgs in iterJobFuncArgs:
31 | self.results[hash(str(jobFuncArgs))] = {
32 | 'state': self.JOB_UNSTART,
33 | 'args': jobFuncArgs,
34 | }
35 | self.totalNum += 1
36 | yield ((jobFunc, jobFuncArgs), {})
37 |
38 | requests = tp.makeRequests(callable_=self._doJob,
39 | args_list=argsGenerator(),
40 | callback=self._cbJobFinished,
41 | exc_callback=self._cbHandleErr)
42 | [self.jobPool.putRequest(req) for req in requests]
43 | self.jobPool.wait()
44 | self.jobPool.dismissWorkers(self.concurrency, do_join=True)
45 | return self.results
46 |
47 | def _doJob(self, jobFuc, jobFuncArgs):
48 | self.results[hash(str(jobFuncArgs))]['state'] = self.JOB_RUNNING
49 | jobRet = jobFuc(*jobFuncArgs) if isinstance(jobFuncArgs, list) \
50 | else jobFuc(jobFuncArgs)
51 | self.results[hash(str(jobFuncArgs))]['jobRet'] = jobRet
52 | return jobRet
53 |
54 | def _cbJobFinished(self, request, result):
55 | self.results[hash(str(request.args[1]))]['state'] = self.JOB_FINISHED
56 | self.successNum += 1
57 |
58 | def _cbHandleErr(self, request, exc_info):
59 | self.results[hash(str(request.args[1]))]['state'] = self.JOB_ERROR
60 | self.results[hash(str(request.args[1]))]['exception'] = str(exc_info[1])
61 | self.errNum += 1
62 |
63 | def handleAbort(self):
64 | for jobId in self.results.keys():
65 | if self.results[jobId]['state'] in (self.JOB_RUNNING,
66 | self.JOB_UNSTART):
67 | self.results[jobId]['state'] = self.JOB_ABORT
68 |
69 | if __name__ == '__main__':
70 | # testing code
71 | import time
72 | from random import randint
73 | from pprint import pprint
74 |
75 | def test(index, sleepTime):
76 | time.sleep(sleepTime)
77 | if sleepTime % 3 == 0:
78 | raise ValueError("%d - %d : 3's multiple!" % (index,
79 | sleepTime))
80 | else:
81 | return True
82 |
83 | try:
84 | wp = WorkerPool(concurrency=10)
85 | wp.work([(i, randint(1, 3)) for i in xrange(20)], test, timeout=None)
86 | pprint(wp.results)
87 | except KeyboardInterrupt, SystemExit:
88 | wp.handleAbort()
89 | pprint(wp.results)
90 | exit()
91 | finally:
92 | print 'errNum %d, successNum %d, totalNum %d' % \
93 | (wp.errNum, wp.successNum, wp.totalNum)
94 |
--------------------------------------------------------------------------------
/lib/cmd.py:
--------------------------------------------------------------------------------
1 | """A generic class to build line-oriented command interpreters.
2 |
3 | Interpreters constructed with this class obey the following conventions:
4 |
5 | 1. End of file on input is processed as the command 'EOF'.
6 | 2. A command is parsed out of each line by collecting the prefix composed
7 | of characters in the identchars member.
8 | 3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method
9 | is passed a single argument consisting of the remainder of the line.
10 | 4. Typing an empty line repeats the last command. (Actually, it calls the
11 | method `emptyline', which may be overridden in a subclass.)
12 | 5. There is a predefined `help' method. Given an argument `topic', it
13 | calls the command `help_topic'. With no arguments, it lists all topics
14 | with defined help_ functions, broken into up to three topics; documented
15 | commands, miscellaneous help topics, and undocumented commands.
16 | 6. The command '?' is a synonym for `help'. The command '!' is a synonym
17 | for `shell', if a do_shell method exists.
18 | 7. If completion is enabled, completing commands will be done automatically,
19 | and completing of commands args is done by calling complete_foo() with
20 | arguments text, line, begidx, endidx. text is string we are matching
21 | against, all returned matches must begin with it. line is the current
22 | input line (lstripped), begidx and endidx are the beginning and end
23 | indexes of the text being matched, which could be used to provide
24 | different completion depending upon which position the argument is in.
25 |
26 | The `default' method may be overridden to intercept commands for which there
27 | is no do_ method.
28 |
29 | The `completedefault' method may be overridden to intercept completions for
30 | commands that have no complete_ method.
31 |
32 | The data member `self.ruler' sets the character used to draw separator lines
33 | in the help messages. If empty, no ruler line is drawn. It defaults to "=".
34 |
35 | If the value of `self.intro' is nonempty when the cmdloop method is called,
36 | it is printed out on interpreter startup. This value may be overridden
37 | via an optional argument to the cmdloop() method.
38 |
39 | The data members `self.doc_header', `self.misc_header', and
40 | `self.undoc_header' set the headers used for the help function's
41 | listings of documented functions, miscellaneous topics, and undocumented
42 | functions respectively.
43 |
44 | These interpreters use raw_input; thus, if the readline module is loaded,
45 | they automatically support Emacs-like command history and editing features.
46 | """
47 |
48 | import string
49 |
50 | __all__ = ["Cmd"]
51 |
52 | PROMPT = '(Cmd) '
53 | IDENTCHARS = string.ascii_letters + string.digits + '_'
54 |
55 | class Cmd(object):
56 | """A simple framework for writing line-oriented command interpreters.
57 |
58 | These are often useful for test harnesses, administrative tools, and
59 | prototypes that will later be wrapped in a more sophisticated interface.
60 |
61 | A Cmd instance or subclass instance is a line-oriented interpreter
62 | framework. There is no good reason to instantiate Cmd itself; rather,
63 | it's useful as a superclass of an interpreter class you define yourself
64 | in order to inherit Cmd's methods and encapsulate action methods.
65 |
66 | """
67 | prompt = PROMPT
68 | identchars = IDENTCHARS
69 | ruler = '='
70 | lastcmd = ''
71 | intro = None
72 | doc_leader = ""
73 | doc_header = "Documented commands (type help ):"
74 | misc_header = "Miscellaneous help topics:"
75 | undoc_header = "Undocumented commands:"
76 | nohelp = "*** No help on %s"
77 | use_rawinput = 1
78 |
79 | def __init__(self, completekey='tab', stdin=None, stdout=None):
80 | """Instantiate a line-oriented interpreter framework.
81 |
82 | The optional argument 'completekey' is the readline name of a
83 | completion key; it defaults to the Tab key. If completekey is
84 | not None and the readline module is available, command completion
85 | is done automatically. The optional arguments stdin and stdout
86 | specify alternate input and output file objects; if not specified,
87 | sys.stdin and sys.stdout are used.
88 |
89 | """
90 | import sys
91 | if stdin is not None:
92 | self.stdin = stdin
93 | else:
94 | self.stdin = sys.stdin
95 | if stdout is not None:
96 | self.stdout = stdout
97 | else:
98 | self.stdout = sys.stdout
99 | self.cmdqueue = []
100 | self.completekey = completekey
101 |
102 | def cmdloop(self, intro=None):
103 | """Repeatedly issue a prompt, accept input, parse an initial prefix
104 | off the received input, and dispatch to action methods, passing them
105 | the remainder of the line as argument.
106 |
107 | """
108 |
109 | self.preloop()
110 | if self.use_rawinput and self.completekey:
111 | try:
112 | import readline
113 | self.old_completer = readline.get_completer()
114 | readline.set_completer(self.complete)
115 | readline.parse_and_bind(self.completekey+": complete")
116 | except ImportError:
117 | pass
118 | try:
119 | if intro is not None:
120 | self.intro = intro
121 | if self.intro:
122 | self.stdout.write(str(self.intro)+"\n")
123 | stop = None
124 | while not stop:
125 | if self.cmdqueue:
126 | line = self.cmdqueue.pop(0)
127 | else:
128 | if self.use_rawinput:
129 | try:
130 | line = raw_input(self.prompt)
131 | except EOFError:
132 | line = 'EOF'
133 | else:
134 | self.stdout.write(self.prompt)
135 | self.stdout.flush()
136 | line = self.stdin.readline()
137 | if not len(line):
138 | line = 'EOF'
139 | else:
140 | line = line.rstrip('\r\n')
141 | line = self.precmd(line)
142 | stop = self.onecmd(line)
143 | stop = self.postcmd(stop, line)
144 | self.postloop()
145 | finally:
146 | if self.use_rawinput and self.completekey:
147 | try:
148 | import readline
149 | readline.set_completer(self.old_completer)
150 | except ImportError:
151 | pass
152 |
153 |
154 | def precmd(self, line):
155 | """Hook method executed just before the command line is
156 | interpreted, but after the input prompt is generated and issued.
157 |
158 | """
159 | return line
160 |
161 | def postcmd(self, stop, line):
162 | """Hook method executed just after a command dispatch is finished."""
163 | return stop
164 |
165 | def preloop(self):
166 | """Hook method executed once when the cmdloop() method is called."""
167 | pass
168 |
169 | def postloop(self):
170 | """Hook method executed once when the cmdloop() method is about to
171 | return.
172 |
173 | """
174 | pass
175 |
176 | def parseline(self, line):
177 | """Parse the line into a command name and a string containing
178 | the arguments. Returns a tuple containing (command, args, line).
179 | 'command' and 'args' may be None if the line couldn't be parsed.
180 | """
181 | line = line.strip()
182 | if not line:
183 | return None, None, line
184 | elif line[0] == '?':
185 | line = 'help ' + line[1:]
186 | elif line[0] == '!':
187 | if hasattr(self, 'do_shell'):
188 | line = 'shell ' + line[1:]
189 | else:
190 | return None, None, line
191 | i, n = 0, len(line)
192 | while i < n and line[i] in self.identchars: i = i+1
193 | cmd, arg = line[:i], line[i:].strip()
194 | return cmd, arg, line
195 |
196 | def onecmd(self, line):
197 | """Interpret the argument as though it had been typed in response
198 | to the prompt.
199 |
200 | This may be overridden, but should not normally need to be;
201 | see the precmd() and postcmd() methods for useful execution hooks.
202 | The return value is a flag indicating whether interpretation of
203 | commands by the interpreter should stop.
204 |
205 | """
206 | cmd, arg, line = self.parseline(line)
207 | if not line:
208 | return self.emptyline()
209 | if cmd is None:
210 | return self.default(line)
211 | self.lastcmd = line
212 | if line == 'EOF' :
213 | self.lastcmd = ''
214 | if cmd == '':
215 | return self.default(line)
216 | else:
217 | try:
218 | func = getattr(self, 'do_' + cmd)
219 | except AttributeError:
220 | return self.default(line)
221 | return func(arg)
222 |
223 | def emptyline(self):
224 | """Called when an empty line is entered in response to the prompt.
225 |
226 | If this method is not overridden, it repeats the last nonempty
227 | command entered.
228 |
229 | """
230 | if self.lastcmd:
231 | return self.onecmd(self.lastcmd)
232 |
233 | def default(self, line):
234 | """Called on an input line when the command prefix is not recognized.
235 |
236 | If this method is not overridden, it prints an error message and
237 | returns.
238 |
239 | """
240 | self.stdout.write('*** Unknown syntax: %s\n'%line)
241 |
242 | def completedefault(self, *ignored):
243 | """Method called to complete an input line when no command-specific
244 | complete_*() method is available.
245 |
246 | By default, it returns an empty list.
247 |
248 | """
249 | return []
250 |
251 | def completenames(self, text, *ignored):
252 | dotext = 'do_'+text
253 | return [a[3:] for a in self.get_names() if a.startswith(dotext)]
254 |
255 | def complete(self, text, state):
256 | """Return the next possible completion for 'text'.
257 |
258 | If a command has not been entered, then complete against command list.
259 | Otherwise try to call complete_ to get list of completions.
260 | """
261 | if state == 0:
262 | import readline
263 | origline = readline.get_line_buffer()
264 | line = origline.lstrip()
265 | stripped = len(origline) - len(line)
266 | begidx = readline.get_begidx() - stripped
267 | endidx = readline.get_endidx() - stripped
268 | if begidx>0:
269 | cmd, args, foo = self.parseline(line)
270 | if cmd == '':
271 | compfunc = self.completedefault
272 | else:
273 | try:
274 | compfunc = getattr(self, 'complete_' + cmd)
275 | except AttributeError:
276 | compfunc = self.completedefault
277 | else:
278 | compfunc = self.completenames
279 | self.completion_matches = compfunc(text, line, begidx, endidx)
280 | try:
281 | return self.completion_matches[state]
282 | except IndexError:
283 | return None
284 |
285 | def get_names(self):
286 | # This method used to pull in base class attributes
287 | # at a time dir() didn't do it yet.
288 | return dir(self.__class__)
289 |
290 | def complete_help(self, *args):
291 | commands = set(self.completenames(*args))
292 | topics = set(a[5:] for a in self.get_names()
293 | if a.startswith('help_' + args[0]))
294 | return list(commands | topics)
295 |
296 | def do_help(self, arg):
297 | 'List available commands with "help" or detailed help with "help cmd".'
298 | if arg:
299 | # XXX check arg syntax
300 | try:
301 | func = getattr(self, 'help_' + arg)
302 | except AttributeError:
303 | try:
304 | doc=getattr(self, 'do_' + arg).__doc__
305 | if doc:
306 | self.stdout.write("%s\n"%str(doc))
307 | return
308 | except AttributeError:
309 | pass
310 | self.stdout.write("%s\n"%str(self.nohelp % (arg,)))
311 | return
312 | func()
313 | else:
314 | names = self.get_names()
315 | cmds_doc = []
316 | cmds_undoc = []
317 | help = {}
318 | for name in names:
319 | if name[:5] == 'help_':
320 | help[name[5:]]=1
321 | names.sort()
322 | # There can be duplicates if routines overridden
323 | prevname = ''
324 | for name in names:
325 | if name[:3] == 'do_':
326 | if name == prevname:
327 | continue
328 | prevname = name
329 | cmd=name[3:]
330 | if cmd in help:
331 | cmds_doc.append(cmd)
332 | del help[cmd]
333 | elif getattr(self, name).__doc__:
334 | cmds_doc.append(cmd)
335 | else:
336 | cmds_undoc.append(cmd)
337 | self.stdout.write("%s\n"%str(self.doc_leader))
338 | self.print_topics(self.doc_header, cmds_doc, 15,80)
339 | self.print_topics(self.misc_header, help.keys(),15,80)
340 | self.print_topics(self.undoc_header, cmds_undoc, 15,80)
341 |
342 | def print_topics(self, header, cmds, cmdlen, maxcol):
343 | if cmds:
344 | self.stdout.write("%s\n"%str(header))
345 | if self.ruler:
346 | self.stdout.write("%s\n"%str(self.ruler * len(header)))
347 | self.columnize(cmds, maxcol-1)
348 | self.stdout.write("\n")
349 |
350 | def columnize(self, list, displaywidth=80):
351 | """Display a list of strings as a compact set of columns.
352 |
353 | Each column is only as wide as necessary.
354 | Columns are separated by two spaces (one was not legible enough).
355 | """
356 | if not list:
357 | self.stdout.write("\n")
358 | return
359 | nonstrings = [i for i in range(len(list))
360 | if not isinstance(list[i], str)]
361 | if nonstrings:
362 | raise TypeError, ("list[i] not a string for i in %s" %
363 | ", ".join(map(str, nonstrings)))
364 | size = len(list)
365 | if size == 1:
366 | self.stdout.write('%s\n'%str(list[0]))
367 | return
368 | # Try every row count from 1 upwards
369 | for nrows in range(1, len(list)):
370 | ncols = (size+nrows-1) // nrows
371 | colwidths = []
372 | totwidth = -2
373 | for col in range(ncols):
374 | colwidth = 0
375 | for row in range(nrows):
376 | i = row + nrows*col
377 | if i >= size:
378 | break
379 | x = list[i]
380 | colwidth = max(colwidth, len(x))
381 | colwidths.append(colwidth)
382 | totwidth += colwidth + 2
383 | if totwidth > displaywidth:
384 | break
385 | if totwidth <= displaywidth:
386 | break
387 | else:
388 | nrows = len(list)
389 | ncols = 1
390 | colwidths = [0]
391 | for row in range(nrows):
392 | texts = []
393 | for col in range(ncols):
394 | i = row + nrows*col
395 | if i >= size:
396 | x = ""
397 | else:
398 | x = list[i]
399 | texts.append(x)
400 | while texts and not texts[-1]:
401 | del texts[-1]
402 | for col in range(len(texts)):
403 | texts[col] = texts[col].ljust(colwidths[col])
404 | self.stdout.write("%s\n"%str(" ".join(texts)))
405 |
--------------------------------------------------------------------------------
/lib/cmd2.py:
--------------------------------------------------------------------------------
1 | """Variant on standard library's cmd with extra features.
2 |
3 | To use, simply import cmd2.Cmd instead of cmd.Cmd; use precisely as though you
4 | were using the standard library's cmd, while enjoying the extra features.
5 |
6 | Searchable command history (commands: "hi", "li", "run")
7 | Load commands from file, save to file, edit commands in file
8 | Multi-line commands
9 | Case-insensitive commands
10 | Special-character shortcut commands (beyond cmd's "@" and "!")
11 | Settable environment parameters
12 | Optional _onchange_{paramname} called when environment parameter changes
13 | Parsing commands with `optparse` options (flags)
14 | Redirection to file with >, >>; input from file with <
15 | Easy transcript-based testing of applications (see example/example.py)
16 | Bash-style ``select`` available
17 |
18 | Note that redirection with > and | will only work if `self.stdout.write()`
19 | is used in place of `print`. The standard library's `cmd` module is
20 | written to use `self.stdout.write()`,
21 |
22 | - Catherine Devlin, Jan 03 2008 - catherinedevlin.blogspot.com
23 |
24 | mercurial repository at http://www.assembla.com/wiki/show/python-cmd2
25 | """
26 | import cmd
27 | import re
28 | import os
29 | import sys
30 | import optparse
31 | import subprocess
32 | import tempfile
33 | import doctest
34 | import unittest
35 | import datetime
36 | import urllib
37 | import glob
38 | import traceback
39 | import platform
40 | import copy
41 | from code import InteractiveConsole, InteractiveInterpreter
42 | from optparse import make_option
43 | import pyparsing
44 |
45 | __version__ = '0.6.8'
46 |
47 | if sys.version_info[0] == 2:
48 | pyparsing.ParserElement.enablePackrat()
49 |
50 | """
51 | Packrat is causing Python3 errors that I don't understand.
52 |
53 | > /usr/local/Cellar/python3/3.2/lib/python3.2/site-packages/pyparsing-1.5.6-py3.2.egg/pyparsing.py(999)scanString()
54 | -> nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
55 | (Pdb) n
56 | NameError: global name 'exc' is not defined
57 |
58 | (Pdb) parseFn
59 |
60 |
61 | Bug report filed: https://sourceforge.net/tracker/?func=detail&atid=617311&aid=3381439&group_id=97203
62 | """
63 |
64 | class OptionParser(optparse.OptionParser):
65 | def exit(self, status=0, msg=None):
66 | self.values._exit = True
67 | if msg:
68 | print (msg)
69 |
70 | def print_help(self, *args, **kwargs):
71 | try:
72 | if self._func.__doc__:
73 | print (self._func.__doc__)
74 | except AttributeError:
75 | pass
76 | optparse.OptionParser.print_help(self, *args, **kwargs)
77 |
78 | def error(self, msg):
79 | """error(msg : string)
80 |
81 | Print a usage message incorporating 'msg' to stderr and exit.
82 | If you override this in a subclass, it should not return -- it
83 | should either exit or raise an exception.
84 | """
85 | raise optparse.OptParseError(msg)
86 |
87 | def remaining_args(oldArgs, newArgList):
88 | '''
89 | Preserves the spacing originally in the argument after
90 | the removal of options.
91 |
92 | >>> remaining_args('-f bar bar cow', ['bar', 'cow'])
93 | 'bar cow'
94 | '''
95 | pattern = '\s+'.join(re.escape(a) for a in newArgList) + '\s*$'
96 | matchObj = re.search(pattern, oldArgs)
97 | return oldArgs[matchObj.start():]
98 |
99 | def _attr_get_(obj, attr):
100 | '''Returns an attribute's value, or None (no error) if undefined.
101 | Analagous to .get() for dictionaries. Useful when checking for
102 | value of options that may not have been defined on a given
103 | method.'''
104 | try:
105 | return getattr(obj, attr)
106 | except AttributeError:
107 | return None
108 |
109 | def _which(editor):
110 | try:
111 | return subprocess.Popen(['which', editor], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0]
112 | except OSError:
113 | return None
114 |
115 | optparse.Values.get = _attr_get_
116 |
117 | options_defined = [] # used to distinguish --options from SQL-style --comments
118 |
119 | def options(option_list, arg_desc="arg"):
120 | '''Used as a decorator and passed a list of optparse-style options,
121 | alters a cmd2 method to populate its ``opts`` argument from its
122 | raw text argument.
123 |
124 | Example: transform
125 | def do_something(self, arg):
126 |
127 | into
128 | @options([make_option('-q', '--quick', action="store_true",
129 | help="Makes things fast")],
130 | "source dest")
131 | def do_something(self, arg, opts):
132 | if opts.quick:
133 | self.fast_button = True
134 | '''
135 | if not isinstance(option_list, list):
136 | option_list = [option_list]
137 | for opt in option_list:
138 | options_defined.append(pyparsing.Literal(opt.get_opt_string()))
139 | def option_setup(func):
140 | optionParser = OptionParser()
141 | for opt in option_list:
142 | optionParser.add_option(opt)
143 | optionParser.set_usage("%s [options] %s" % (func.__name__[3:], arg_desc))
144 | optionParser._func = func
145 | def new_func(instance, arg):
146 | try:
147 | opts, newArgList = optionParser.parse_args(arg.split())
148 | # Must find the remaining args in the original argument list, but
149 | # mustn't include the command itself
150 | #if hasattr(arg, 'parsed') and newArgList[0] == arg.parsed.command:
151 | # newArgList = newArgList[1:]
152 | newArgs = remaining_args(arg, newArgList)
153 | if isinstance(arg, ParsedString):
154 | arg = arg.with_args_replaced(newArgs)
155 | else:
156 | arg = newArgs
157 | except optparse.OptParseError as e:
158 | print (e)
159 | optionParser.print_help()
160 | return
161 | if hasattr(opts, '_exit'):
162 | return None
163 | result = func(instance, arg, opts)
164 | return result
165 | new_func.__doc__ = '%s\n%s' % (func.__doc__, optionParser.format_help())
166 | return new_func
167 | return option_setup
168 |
169 | class PasteBufferError(EnvironmentError):
170 | if sys.platform[:3] == 'win':
171 | errmsg = """Redirecting to or from paste buffer requires pywin32
172 | to be installed on operating system.
173 | Download from http://sourceforge.net/projects/pywin32/"""
174 | elif sys.platform[:3] == 'dar':
175 | # Use built in pbcopy on Mac OSX
176 | pass
177 | else:
178 | errmsg = """Redirecting to or from paste buffer requires xclip
179 | to be installed on operating system.
180 | On Debian/Ubuntu, 'sudo apt-get install xclip' will install it."""
181 | def __init__(self):
182 | Exception.__init__(self, self.errmsg)
183 |
184 | pastebufferr = """Redirecting to or from paste buffer requires %s
185 | to be installed on operating system.
186 | %s"""
187 |
188 | if subprocess.mswindows:
189 | try:
190 | import win32clipboard
191 | def get_paste_buffer():
192 | win32clipboard.OpenClipboard(0)
193 | try:
194 | result = win32clipboard.GetClipboardData()
195 | except TypeError:
196 | result = '' #non-text
197 | win32clipboard.CloseClipboard()
198 | return result
199 | def write_to_paste_buffer(txt):
200 | win32clipboard.OpenClipboard(0)
201 | win32clipboard.EmptyClipboard()
202 | win32clipboard.SetClipboardText(txt)
203 | win32clipboard.CloseClipboard()
204 | except ImportError:
205 | def get_paste_buffer(*args):
206 | raise OSError(pastebufferr % ('pywin32', 'Download from http://sourceforge.net/projects/pywin32/'))
207 | write_to_paste_buffer = get_paste_buffer
208 | elif sys.platform == 'darwin':
209 | can_clip = False
210 | try:
211 | # test for pbcopy - AFAIK, should always be installed on MacOS
212 | subprocess.check_call('pbcopy -help', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
213 | can_clip = True
214 | except (subprocess.CalledProcessError, OSError, IOError):
215 | pass
216 | if can_clip:
217 | def get_paste_buffer():
218 | pbcopyproc = subprocess.Popen('pbcopy -help', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
219 | return pbcopyproc.stdout.read()
220 | def write_to_paste_buffer(txt):
221 | pbcopyproc = subprocess.Popen('pbcopy', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
222 | pbcopyproc.communicate(txt.encode())
223 | else:
224 | def get_paste_buffer(*args):
225 | raise OSError(pastebufferr % ('pbcopy', 'On MacOS X - error should not occur - part of the default installation'))
226 | write_to_paste_buffer = get_paste_buffer
227 | else:
228 | can_clip = False
229 | try:
230 | subprocess.check_call('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
231 | can_clip = True
232 | except AttributeError: # check_call not defined, Python < 2.5
233 | try:
234 | teststring = 'Testing for presence of xclip.'
235 | xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
236 | xclipproc.stdin.write(teststring)
237 | xclipproc.stdin.close()
238 | xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
239 | if xclipproc.stdout.read() == teststring:
240 | can_clip = True
241 | except Exception: # hate a bare Exception call, but exception classes vary too much b/t stdlib versions
242 | pass
243 | except Exception:
244 | pass # something went wrong with xclip and we cannot use it
245 | if can_clip:
246 | def get_paste_buffer():
247 | xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
248 | return xclipproc.stdout.read()
249 | def write_to_paste_buffer(txt):
250 | xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
251 | xclipproc.stdin.write(txt.encode())
252 | xclipproc.stdin.close()
253 | # but we want it in both the "primary" and "mouse" clipboards
254 | xclipproc = subprocess.Popen('xclip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
255 | xclipproc.stdin.write(txt.encode())
256 | xclipproc.stdin.close()
257 | else:
258 | def get_paste_buffer(*args):
259 | raise OSError(pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"'))
260 | write_to_paste_buffer = get_paste_buffer
261 |
262 | pyparsing.ParserElement.setDefaultWhitespaceChars(' \t')
263 |
264 | class ParsedString(str):
265 | def full_parsed_statement(self):
266 | new = ParsedString('%s %s' % (self.parsed.command, self.parsed.args))
267 | new.parsed = self.parsed
268 | new.parser = self.parser
269 | return new
270 | def with_args_replaced(self, newargs):
271 | new = ParsedString(newargs)
272 | new.parsed = self.parsed
273 | new.parser = self.parser
274 | new.parsed['args'] = newargs
275 | new.parsed.statement['args'] = newargs
276 | return new
277 |
278 | class StubbornDict(dict):
279 | '''Dictionary that tolerates many input formats.
280 | Create it with stubbornDict(arg) factory function.
281 |
282 | >>> d = StubbornDict(large='gross', small='klein')
283 | >>> sorted(d.items())
284 | [('large', 'gross'), ('small', 'klein')]
285 | >>> d.append(['plain', ' plaid'])
286 | >>> sorted(d.items())
287 | [('large', 'gross'), ('plaid', ''), ('plain', ''), ('small', 'klein')]
288 | >>> d += ' girl Frauelein, Maedchen\\n\\n shoe schuh'
289 | >>> sorted(d.items())
290 | [('girl', 'Frauelein, Maedchen'), ('large', 'gross'), ('plaid', ''), ('plain', ''), ('shoe', 'schuh'), ('small', 'klein')]
291 | '''
292 | def update(self, arg):
293 | dict.update(self, StubbornDict.to_dict(arg))
294 | append = update
295 | def __iadd__(self, arg):
296 | self.update(arg)
297 | return self
298 | def __add__(self, arg):
299 | selfcopy = copy.copy(self)
300 | selfcopy.update(stubbornDict(arg))
301 | return selfcopy
302 | def __radd__(self, arg):
303 | selfcopy = copy.copy(self)
304 | selfcopy.update(stubbornDict(arg))
305 | return selfcopy
306 |
307 | @classmethod
308 | def to_dict(cls, arg):
309 | 'Generates dictionary from string or list of strings'
310 | if hasattr(arg, 'splitlines'):
311 | arg = arg.splitlines()
312 | if hasattr(arg, '__reversed__'):
313 | result = {}
314 | for a in arg:
315 | a = a.strip()
316 | if a:
317 | key_val = a.split(None, 1)
318 | key = key_val[0]
319 | if len(key_val) > 1:
320 | val = key_val[1]
321 | else:
322 | val = ''
323 | result[key] = val
324 | else:
325 | result = arg
326 | return result
327 |
328 | def stubbornDict(*arg, **kwarg):
329 | '''
330 | >>> sorted(stubbornDict('cow a bovine\\nhorse an equine').items())
331 | [('cow', 'a bovine'), ('horse', 'an equine')]
332 | >>> sorted(stubbornDict(['badger', 'porcupine a poky creature']).items())
333 | [('badger', ''), ('porcupine', 'a poky creature')]
334 | >>> sorted(stubbornDict(turtle='has shell', frog='jumpy').items())
335 | [('frog', 'jumpy'), ('turtle', 'has shell')]
336 | '''
337 | result = {}
338 | for a in arg:
339 | result.update(StubbornDict.to_dict(a))
340 | result.update(kwarg)
341 | return StubbornDict(result)
342 |
343 | def replace_with_file_contents(fname):
344 | if fname:
345 | try:
346 | result = open(os.path.expanduser(fname[0])).read()
347 | except IOError:
348 | result = '< %s' % fname[0] # wasn't a file after all
349 | else:
350 | result = get_paste_buffer()
351 | return result
352 |
353 | class EmbeddedConsoleExit(SystemExit):
354 | pass
355 |
356 | class EmptyStatement(Exception):
357 | pass
358 |
359 | def ljust(x, width, fillchar=' '):
360 | 'analogous to str.ljust, but works for lists'
361 | if hasattr(x, 'ljust'):
362 | return x.ljust(width, fillchar)
363 | else:
364 | if len(x) < width:
365 | x = (x + [fillchar] * width)[:width]
366 | return x
367 |
368 | class Cmd(cmd.Cmd):
369 | echo = False
370 | case_insensitive = True # Commands recognized regardless of case
371 | continuation_prompt = '> '
372 | timing = False # Prints elapsed time for each command
373 | # make sure your terminators are not in legalChars!
374 | legalChars = u'!#$%.:?@_' + pyparsing.alphanums + pyparsing.alphas8bit
375 | shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
376 | excludeFromHistory = '''run r list l history hi ed edit li eof'''.split()
377 | default_to_shell = False
378 | noSpecialParse = 'set ed edit exit'.split()
379 | defaultExtension = 'txt' # For ``save``, ``load``, etc.
380 | default_file_name = 'command.txt' # For ``save``, ``load``, etc.
381 | abbrev = True # Abbreviated commands recognized
382 | current_script_dir = None
383 | reserved_words = []
384 | feedback_to_output = False # Do include nonessentials in >, | output
385 | quiet = False # Do not suppress nonessential output
386 | debug = False
387 | locals_in_py = True
388 | kept_state = None
389 | redirector = '>' # for sending output to file
390 | settable = stubbornDict('''
391 | prompt
392 | colors Colorized output (*nix only)
393 | continuation_prompt On 2nd+ line of input
394 | debug Show full error stack on error
395 | default_file_name for ``save``, ``load``, etc.
396 | editor Program used by ``edit``
397 | case_insensitive upper- and lower-case both OK
398 | feedback_to_output include nonessentials in `|`, `>` results
399 | quiet Don't print nonessential feedback
400 | echo Echo command issued into output
401 | timing Report execution times
402 | abbrev Accept abbreviated commands
403 | ''')
404 |
405 | def poutput(self, msg):
406 | '''Convenient shortcut for self.stdout.write(); adds newline if necessary.'''
407 | if msg:
408 | self.stdout.write(msg)
409 | if msg[-1] != '\n':
410 | self.stdout.write('\n')
411 | def perror(self, errmsg, statement=None):
412 | if self.debug:
413 | traceback.print_exc()
414 | print (str(errmsg))
415 | def pfeedback(self, msg):
416 | """For printing nonessential feedback. Can be silenced with `quiet`.
417 | Inclusion in redirected output is controlled by `feedback_to_output`."""
418 | if not self.quiet:
419 | if self.feedback_to_output:
420 | self.poutput(msg)
421 | else:
422 | print (msg)
423 | _STOP_AND_EXIT = True # distinguish end of script file from actual exit
424 | _STOP_SCRIPT_NO_EXIT = -999
425 | editor = os.environ.get('EDITOR')
426 | if not editor:
427 | if sys.platform[:3] == 'win':
428 | editor = 'notepad'
429 | else:
430 | for editor in ['gedit', 'kate', 'vim', 'vi', 'emacs', 'nano', 'pico']:
431 | if _which(editor):
432 | break
433 |
434 | colorcodes = {'bold':{True:'\x1b[1m',False:'\x1b[22m'},
435 | 'cyan':{True:'\x1b[36m',False:'\x1b[39m'},
436 | 'blue':{True:'\x1b[34m',False:'\x1b[39m'},
437 | 'red':{True:'\x1b[31m',False:'\x1b[39m'},
438 | 'magenta':{True:'\x1b[35m',False:'\x1b[39m'},
439 | 'green':{True:'\x1b[32m',False:'\x1b[39m'},
440 | 'underline':{True:'\x1b[4m',False:'\x1b[24m'}}
441 | colors = (platform.system() != 'Windows')
442 | def colorize(self, val, color):
443 | '''Given a string (``val``), returns that string wrapped in UNIX-style
444 | special characters that turn on (and then off) text color and style.
445 | If the ``colors`` environment paramter is ``False``, or the application
446 | is running on Windows, will return ``val`` unchanged.
447 | ``color`` should be one of the supported strings (or styles):
448 | red/blue/green/cyan/magenta, bold, underline'''
449 | if self.colors and (self.stdout == self.initial_stdout):
450 | return self.colorcodes[color][True] + val + self.colorcodes[color][False]
451 | return val
452 |
453 | def do_cmdenvironment(self, args):
454 | '''Summary report of interactive parameters.'''
455 | self.stdout.write("""
456 | Commands are %(casesensitive)scase-sensitive.
457 | Commands may be terminated with: %(terminators)s
458 | Settable parameters: %(settable)s\n""" % \
459 | { 'casesensitive': (self.case_insensitive and 'not ') or '',
460 | 'terminators': str(self.terminators),
461 | 'settable': ' '.join(self.settable)
462 | })
463 |
464 | def do_help(self, arg):
465 | if arg:
466 | funcname = self.func_named(arg)
467 | if funcname:
468 | fn = getattr(self, funcname)
469 | try:
470 | fn.optionParser.print_help(file=self.stdout)
471 | except AttributeError:
472 | cmd.Cmd.do_help(self, funcname[3:])
473 | else:
474 | cmd.Cmd.do_help(self, arg)
475 |
476 | def __init__(self, *args, **kwargs):
477 | cmd.Cmd.__init__(self, *args, **kwargs)
478 | self.initial_stdout = sys.stdout
479 | self.history = History()
480 | self.pystate = {}
481 | self.shortcuts = sorted(self.shortcuts.items(), reverse=True)
482 | self.keywords = self.reserved_words + [fname[3:] for fname in dir(self)
483 | if fname.startswith('do_')]
484 | self._init_parser()
485 |
486 | def do_shortcuts(self, args):
487 | """Lists single-key shortcuts available."""
488 | result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in sorted(self.shortcuts))
489 | self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result))
490 |
491 | prefixParser = pyparsing.Empty()
492 | commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment])
493 | commentGrammars.addParseAction(lambda x: '')
494 | commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(
495 | pyparsing.stringEnd ^ '*/')
496 | terminators = [';']
497 | blankLinesAllowed = False
498 | multilineCommands = []
499 |
500 | def _init_parser(self):
501 | r'''
502 | >>> c = Cmd()
503 | >>> c.multilineCommands = ['multiline']
504 | >>> c.case_insensitive = True
505 | >>> c._init_parser()
506 | >>> print (c.parser.parseString('').dump())
507 | []
508 | >>> print (c.parser.parseString('').dump())
509 | []
510 | >>> print (c.parser.parseString('/* empty command */').dump())
511 | []
512 | >>> print (c.parser.parseString('plainword').dump())
513 | ['plainword', '']
514 | - command: plainword
515 | - statement: ['plainword', '']
516 | - command: plainword
517 | >>> print (c.parser.parseString('termbare;').dump())
518 | ['termbare', '', ';', '']
519 | - command: termbare
520 | - statement: ['termbare', '', ';']
521 | - command: termbare
522 | - terminator: ;
523 | - terminator: ;
524 | >>> print (c.parser.parseString('termbare; suffx').dump())
525 | ['termbare', '', ';', 'suffx']
526 | - command: termbare
527 | - statement: ['termbare', '', ';']
528 | - command: termbare
529 | - terminator: ;
530 | - suffix: suffx
531 | - terminator: ;
532 | >>> print (c.parser.parseString('barecommand').dump())
533 | ['barecommand', '']
534 | - command: barecommand
535 | - statement: ['barecommand', '']
536 | - command: barecommand
537 | >>> print (c.parser.parseString('COMmand with args').dump())
538 | ['command', 'with args']
539 | - args: with args
540 | - command: command
541 | - statement: ['command', 'with args']
542 | - args: with args
543 | - command: command
544 | >>> print (c.parser.parseString('command with args and terminator; and suffix').dump())
545 | ['command', 'with args and terminator', ';', 'and suffix']
546 | - args: with args and terminator
547 | - command: command
548 | - statement: ['command', 'with args and terminator', ';']
549 | - args: with args and terminator
550 | - command: command
551 | - terminator: ;
552 | - suffix: and suffix
553 | - terminator: ;
554 | >>> print (c.parser.parseString('simple | piped').dump())
555 | ['simple', '', '|', ' piped']
556 | - command: simple
557 | - pipeTo: piped
558 | - statement: ['simple', '']
559 | - command: simple
560 | >>> print (c.parser.parseString('double-pipe || is not a pipe').dump())
561 | ['double', '-pipe || is not a pipe']
562 | - args: -pipe || is not a pipe
563 | - command: double
564 | - statement: ['double', '-pipe || is not a pipe']
565 | - args: -pipe || is not a pipe
566 | - command: double
567 | >>> print (c.parser.parseString('command with args, terminator;sufx | piped').dump())
568 | ['command', 'with args, terminator', ';', 'sufx', '|', ' piped']
569 | - args: with args, terminator
570 | - command: command
571 | - pipeTo: piped
572 | - statement: ['command', 'with args, terminator', ';']
573 | - args: with args, terminator
574 | - command: command
575 | - terminator: ;
576 | - suffix: sufx
577 | - terminator: ;
578 | >>> print (c.parser.parseString('output into > afile.txt').dump())
579 | ['output', 'into', '>', 'afile.txt']
580 | - args: into
581 | - command: output
582 | - output: >
583 | - outputTo: afile.txt
584 | - statement: ['output', 'into']
585 | - args: into
586 | - command: output
587 | >>> print (c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump())
588 | ['output', 'into', ';', 'sufx', '|', ' pipethrume plz', '>', 'afile.txt']
589 | - args: into
590 | - command: output
591 | - output: >
592 | - outputTo: afile.txt
593 | - pipeTo: pipethrume plz
594 | - statement: ['output', 'into', ';']
595 | - args: into
596 | - command: output
597 | - terminator: ;
598 | - suffix: sufx
599 | - terminator: ;
600 | >>> print (c.parser.parseString('output to paste buffer >> ').dump())
601 | ['output', 'to paste buffer', '>>', '']
602 | - args: to paste buffer
603 | - command: output
604 | - output: >>
605 | - statement: ['output', 'to paste buffer']
606 | - args: to paste buffer
607 | - command: output
608 | >>> print (c.parser.parseString('ignore the /* commented | > */ stuff;').dump())
609 | ['ignore', 'the /* commented | > */ stuff', ';', '']
610 | - args: the /* commented | > */ stuff
611 | - command: ignore
612 | - statement: ['ignore', 'the /* commented | > */ stuff', ';']
613 | - args: the /* commented | > */ stuff
614 | - command: ignore
615 | - terminator: ;
616 | - terminator: ;
617 | >>> print (c.parser.parseString('has > inside;').dump())
618 | ['has', '> inside', ';', '']
619 | - args: > inside
620 | - command: has
621 | - statement: ['has', '> inside', ';']
622 | - args: > inside
623 | - command: has
624 | - terminator: ;
625 | - terminator: ;
626 | >>> print (c.parser.parseString('multiline has > inside an unfinished command').dump())
627 | ['multiline', ' has > inside an unfinished command']
628 | - multilineCommand: multiline
629 | >>> print (c.parser.parseString('multiline has > inside;').dump())
630 | ['multiline', 'has > inside', ';', '']
631 | - args: has > inside
632 | - multilineCommand: multiline
633 | - statement: ['multiline', 'has > inside', ';']
634 | - args: has > inside
635 | - multilineCommand: multiline
636 | - terminator: ;
637 | - terminator: ;
638 | >>> print (c.parser.parseString('multiline command /* with comment in progress;').dump())
639 | ['multiline', ' command /* with comment in progress;']
640 | - multilineCommand: multiline
641 | >>> print (c.parser.parseString('multiline command /* with comment complete */ is done;').dump())
642 | ['multiline', 'command /* with comment complete */ is done', ';', '']
643 | - args: command /* with comment complete */ is done
644 | - multilineCommand: multiline
645 | - statement: ['multiline', 'command /* with comment complete */ is done', ';']
646 | - args: command /* with comment complete */ is done
647 | - multilineCommand: multiline
648 | - terminator: ;
649 | - terminator: ;
650 | >>> print (c.parser.parseString('multiline command ends\n\n').dump())
651 | ['multiline', 'command ends', '\n', '\n']
652 | - args: command ends
653 | - multilineCommand: multiline
654 | - statement: ['multiline', 'command ends', '\n', '\n']
655 | - args: command ends
656 | - multilineCommand: multiline
657 | - terminator: ['\n', '\n']
658 | - terminator: ['\n', '\n']
659 | >>> print (c.parser.parseString('multiline command "with term; ends" now\n\n').dump())
660 | ['multiline', 'command "with term; ends" now', '\n', '\n']
661 | - args: command "with term; ends" now
662 | - multilineCommand: multiline
663 | - statement: ['multiline', 'command "with term; ends" now', '\n', '\n']
664 | - args: command "with term; ends" now
665 | - multilineCommand: multiline
666 | - terminator: ['\n', '\n']
667 | - terminator: ['\n', '\n']
668 | >>> print (c.parser.parseString('what if "quoted strings /* seem to " start comments?').dump())
669 | ['what', 'if "quoted strings /* seem to " start comments?']
670 | - args: if "quoted strings /* seem to " start comments?
671 | - command: what
672 | - statement: ['what', 'if "quoted strings /* seem to " start comments?']
673 | - args: if "quoted strings /* seem to " start comments?
674 | - command: what
675 | '''
676 | #outputParser = (pyparsing.Literal('>>') | (pyparsing.WordStart() + '>') | pyparsing.Regex('[^=]>'))('output')
677 | outputParser = (pyparsing.Literal(self.redirector *2) | \
678 | (pyparsing.WordStart() + self.redirector) | \
679 | pyparsing.Regex('[^=]' + self.redirector))('output')
680 |
681 | terminatorParser = pyparsing.Or([(hasattr(t, 'parseString') and t) or pyparsing.Literal(t) for t in self.terminators])('terminator')
682 | stringEnd = pyparsing.stringEnd ^ '\nEOF'
683 | self.multilineCommand = pyparsing.Or([pyparsing.Keyword(c, caseless=self.case_insensitive) for c in self.multilineCommands])('multilineCommand')
684 | oneLineCommand = (~self.multilineCommand + pyparsing.Word(self.legalChars))('command')
685 | pipe = pyparsing.Keyword('|', identChars='|')
686 | self.commentGrammars.ignore(pyparsing.quotedString).setParseAction(lambda x: '')
687 | doNotParse = self.commentGrammars | self.commentInProgress | pyparsing.quotedString
688 | afterElements = \
689 | pyparsing.Optional(pipe + pyparsing.SkipTo(outputParser ^ stringEnd, ignore=doNotParse)('pipeTo')) + \
690 | pyparsing.Optional(outputParser + pyparsing.SkipTo(stringEnd, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('outputTo'))
691 | if self.case_insensitive:
692 | self.multilineCommand.setParseAction(lambda x: x[0].lower())
693 | oneLineCommand.setParseAction(lambda x: x[0].lower())
694 | if self.blankLinesAllowed:
695 | self.blankLineTerminationParser = pyparsing.NoMatch
696 | else:
697 | self.blankLineTerminator = (pyparsing.lineEnd + pyparsing.lineEnd)('terminator')
698 | self.blankLineTerminator.setResultsName('terminator')
699 | self.blankLineTerminationParser = ((self.multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(self.blankLineTerminator, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('args') + self.blankLineTerminator)('statement')
700 | self.multilineParser = (((self.multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('args') + terminatorParser)('statement') +
701 | pyparsing.SkipTo(outputParser ^ pipe ^ stringEnd, ignore=doNotParse).setParseAction(lambda x: x[0].strip())('suffix') + afterElements)
702 | self.multilineParser.ignore(self.commentInProgress)
703 | self.singleLineParser = ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ pipe ^ outputParser, ignore=doNotParse).setParseAction(lambda x:x[0].strip())('args'))('statement') +
704 | pyparsing.Optional(terminatorParser) + afterElements)
705 | #self.multilineParser = self.multilineParser.setResultsName('multilineParser')
706 | #self.singleLineParser = self.singleLineParser.setResultsName('singleLineParser')
707 | self.blankLineTerminationParser = self.blankLineTerminationParser.setResultsName('statement')
708 | self.parser = self.prefixParser + (
709 | stringEnd |
710 | self.multilineParser |
711 | self.singleLineParser |
712 | self.blankLineTerminationParser |
713 | self.multilineCommand + pyparsing.SkipTo(stringEnd, ignore=doNotParse)
714 | )
715 | self.parser.ignore(self.commentGrammars)
716 |
717 | inputMark = pyparsing.Literal('<')
718 | inputMark.setParseAction(lambda x: '')
719 | fileName = pyparsing.Word(self.legalChars + '/\\')
720 | inputFrom = fileName('inputFrom')
721 | inputFrom.setParseAction(replace_with_file_contents)
722 | # a not-entirely-satisfactory way of distinguishing < as in "import from" from <
723 | # as in "lesser than"
724 | self.inputParser = inputMark + pyparsing.Optional(inputFrom) + pyparsing.Optional('>') + \
725 | pyparsing.Optional(fileName) + (pyparsing.stringEnd | '|')
726 | self.inputParser.ignore(self.commentInProgress)
727 |
728 | def preparse(self, raw, **kwargs):
729 | return raw
730 | def postparse(self, parseResult):
731 | return parseResult
732 |
733 | def parsed(self, raw, **kwargs):
734 | if isinstance(raw, ParsedString):
735 | p = raw
736 | else:
737 | # preparse is an overridable hook; default makes no changes
738 | s = self.preparse(raw, **kwargs)
739 | s = self.inputParser.transformString(s.lstrip())
740 | s = self.commentGrammars.transformString(s)
741 | for (shortcut, expansion) in self.shortcuts:
742 | if s.lower().startswith(shortcut):
743 | s = s.replace(shortcut, expansion + ' ', 1)
744 | break
745 | result = self.parser.parseString(s)
746 | result['raw'] = raw
747 | result['command'] = result.multilineCommand or result.command
748 | result = self.postparse(result)
749 | p = ParsedString(result.args)
750 | p.parsed = result
751 | p.parser = self.parsed
752 | for (key, val) in kwargs.items():
753 | p.parsed[key] = val
754 | return p
755 |
756 | def postparsing_precmd(self, statement):
757 | stop = 0
758 | return stop, statement
759 | def postparsing_postcmd(self, stop):
760 | return stop
761 |
762 | def func_named(self, arg):
763 | result = None
764 | target = 'do_' + arg
765 | if target in dir(self):
766 | result = target
767 | else:
768 | if self.abbrev: # accept shortened versions of commands
769 | funcs = [fname for fname in self.keywords if fname.startswith(arg)]
770 | if len(funcs) == 1:
771 | result = 'do_' + funcs[0]
772 | return result
773 | def onecmd_plus_hooks(self, line):
774 | # The outermost level of try/finally nesting can be condensed once
775 | # Python 2.4 support can be dropped.
776 | stop = 0
777 | try:
778 | try:
779 | statement = self.complete_statement(line)
780 | (stop, statement) = self.postparsing_precmd(statement)
781 | if stop:
782 | return self.postparsing_postcmd(stop)
783 | if statement.parsed.command not in self.excludeFromHistory:
784 | self.history.append(statement.parsed.raw)
785 | try:
786 | self.redirect_output(statement)
787 | timestart = datetime.datetime.now()
788 | statement = self.precmd(statement)
789 | stop = self.onecmd(statement)
790 | stop = self.postcmd(stop, statement)
791 | if self.timing:
792 | self.pfeedback('\nElapsed: %s\n' % str(datetime.datetime.now() - timestart))
793 | finally:
794 | self.restore_output(statement)
795 | except EmptyStatement:
796 | return 0
797 | except Exception as e:
798 | self.perror(str(e), statement)
799 | finally:
800 | return self.postparsing_postcmd(stop)
801 | def complete_statement(self, line):
802 | """Keep accepting lines of input until the command is complete."""
803 | if (not line) or (
804 | not pyparsing.Or(self.commentGrammars).
805 | setParseAction(lambda x: '').transformString(line)):
806 | raise EmptyStatement()
807 | statement = self.parsed(line)
808 | while statement.parsed.multilineCommand and (statement.parsed.terminator == ''):
809 | statement = '%s\n%s' % (statement.parsed.raw,
810 | self.pseudo_raw_input(self.continuation_prompt))
811 | statement = self.parsed(statement)
812 | if not statement.parsed.command:
813 | raise EmptyStatement()
814 | return statement
815 |
816 | def redirect_output(self, statement):
817 | if statement.parsed.pipeTo:
818 | self.kept_state = Statekeeper(self, ('stdout',))
819 | self.kept_sys = Statekeeper(sys, ('stdout',))
820 | self.redirect = subprocess.Popen(statement.parsed.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
821 | sys.stdout = self.stdout = self.redirect.stdin
822 | elif statement.parsed.output:
823 | if (not statement.parsed.outputTo) and (not can_clip):
824 | raise EnvironmentError('Cannot redirect to paste buffer; install ``xclip`` and re-run to enable')
825 | self.kept_state = Statekeeper(self, ('stdout',))
826 | self.kept_sys = Statekeeper(sys, ('stdout',))
827 | if statement.parsed.outputTo:
828 | mode = 'w'
829 | if statement.parsed.output == 2 * self.redirector:
830 | mode = 'a'
831 | sys.stdout = self.stdout = open(os.path.expanduser(statement.parsed.outputTo), mode)
832 | else:
833 | sys.stdout = self.stdout = tempfile.TemporaryFile(mode="w+")
834 | if statement.parsed.output == '>>':
835 | self.stdout.write(get_paste_buffer())
836 |
837 | def restore_output(self, statement):
838 | if self.kept_state:
839 | if statement.parsed.output:
840 | if not statement.parsed.outputTo:
841 | self.stdout.seek(0)
842 | write_to_paste_buffer(self.stdout.read())
843 | elif statement.parsed.pipeTo:
844 | for result in self.redirect.communicate():
845 | self.kept_state.stdout.write(result or '')
846 | self.stdout.close()
847 | self.kept_state.restore()
848 | self.kept_sys.restore()
849 | self.kept_state = None
850 |
851 | def onecmd(self, line):
852 | """Interpret the argument as though it had been typed in response
853 | to the prompt.
854 |
855 | This may be overridden, but should not normally need to be;
856 | see the precmd() and postcmd() methods for useful execution hooks.
857 | The return value is a flag indicating whether interpretation of
858 | commands by the interpreter should stop.
859 |
860 | This (`cmd2`) version of `onecmd` already override's `cmd`'s `onecmd`.
861 |
862 | """
863 | statement = self.parsed(line)
864 | self.lastcmd = statement.parsed.raw
865 | funcname = self.func_named(statement.parsed.command)
866 | if not funcname:
867 | return self._default(statement)
868 | try:
869 | func = getattr(self, funcname)
870 | except AttributeError:
871 | return self._default(statement)
872 | stop = func(statement)
873 | return stop
874 |
875 | def _default(self, statement):
876 | arg = statement.full_parsed_statement()
877 | if self.default_to_shell:
878 | result = os.system(arg)
879 | if not result:
880 | return self.postparsing_postcmd(None)
881 | return self.postparsing_postcmd(self.default(arg))
882 |
883 | def pseudo_raw_input(self, prompt):
884 | """copied from cmd's cmdloop; like raw_input, but accounts for changed stdin, stdout"""
885 |
886 | if self.use_rawinput:
887 | try:
888 | line = raw_input(prompt)
889 | except EOFError:
890 | line = 'EOF'
891 | else:
892 | self.stdout.write(prompt)
893 | self.stdout.flush()
894 | line = self.stdin.readline()
895 | if not len(line):
896 | line = 'EOF'
897 | else:
898 | if line[-1] == '\n': # this was always true in Cmd
899 | line = line[:-1]
900 | return line
901 |
902 | def _cmdloop(self, intro=None):
903 | """Repeatedly issue a prompt, accept input, parse an initial prefix
904 | off the received input, and dispatch to action methods, passing them
905 | the remainder of the line as argument.
906 | """
907 |
908 | # An almost perfect copy from Cmd; however, the pseudo_raw_input portion
909 | # has been split out so that it can be called separately
910 |
911 | self.preloop()
912 | if self.use_rawinput and self.completekey:
913 | try:
914 | import readline
915 | self.old_completer = readline.get_completer()
916 | readline.set_completer(self.complete)
917 | readline.parse_and_bind(self.completekey+": complete")
918 | except ImportError:
919 | pass
920 | try:
921 | if intro is not None:
922 | self.intro = intro
923 | if self.intro:
924 | self.stdout.write(str(self.intro)+"\n")
925 | stop = None
926 | while not stop:
927 | if self.cmdqueue:
928 | line = self.cmdqueue.pop(0)
929 | else:
930 | line = self.pseudo_raw_input(self.prompt)
931 | if (self.echo) and (isinstance(self.stdin, file)):
932 | self.stdout.write(line + '\n')
933 | stop = self.onecmd_plus_hooks(line)
934 | self.postloop()
935 | finally:
936 | if self.use_rawinput and self.completekey:
937 | try:
938 | import readline
939 | readline.set_completer(self.old_completer)
940 | except ImportError:
941 | pass
942 | return stop
943 |
944 | def do_EOF(self, arg):
945 | return self._STOP_SCRIPT_NO_EXIT # End of script; should not exit app
946 | do_eof = do_EOF
947 |
948 | def do_quit(self, arg):
949 | return self._STOP_AND_EXIT
950 | do_exit = do_quit
951 | do_q = do_quit
952 |
953 | def select(self, options, prompt='Your choice? '):
954 | '''Presents a numbered menu to the user. Modelled after
955 | the bash shell's SELECT. Returns the item chosen.
956 |
957 | Argument ``options`` can be:
958 |
959 | | a single string -> will be split into one-word options
960 | | a list of strings -> will be offered as options
961 | | a list of tuples -> interpreted as (value, text), so
962 | that the return value can differ from
963 | the text advertised to the user '''
964 | if isinstance(options, basestring):
965 | options = zip(options.split(), options.split())
966 | fulloptions = []
967 | for opt in options:
968 | if isinstance(opt, basestring):
969 | fulloptions.append((opt, opt))
970 | else:
971 | try:
972 | fulloptions.append((opt[0], opt[1]))
973 | except IndexError:
974 | fulloptions.append((opt[0], opt[0]))
975 | for (idx, (value, text)) in enumerate(fulloptions):
976 | self.poutput(' %2d. %s\n' % (idx+1, text))
977 | while True:
978 | response = raw_input(prompt)
979 | try:
980 | response = int(response)
981 | result = fulloptions[response - 1][0]
982 | break
983 | except ValueError:
984 | pass # loop and ask again
985 | return result
986 |
987 | @options([make_option('-l', '--long', action="store_true",
988 | help="describe function of parameter")])
989 | def do_show(self, arg, opts):
990 | '''Shows value of a parameter.'''
991 | param = arg.strip().lower()
992 | result = {}
993 | maxlen = 0
994 | for p in self.settable:
995 | if (not param) or p.startswith(param):
996 | result[p] = '%s: %s' % (p, str(getattr(self, p)))
997 | maxlen = max(maxlen, len(result[p]))
998 | if result:
999 | for p in sorted(result):
1000 | if opts.long:
1001 | self.poutput('%s # %s' % (result[p].ljust(maxlen), self.settable[p]))
1002 | else:
1003 | self.poutput(result[p])
1004 | else:
1005 | raise NotImplementedError("Parameter '%s' not supported (type 'show' for list of parameters)." % param)
1006 |
1007 | def do_set(self, arg):
1008 | '''
1009 | Sets a cmd2 parameter. Accepts abbreviated parameter names so long
1010 | as there is no ambiguity. Call without arguments for a list of
1011 | settable parameters with their values.'''
1012 | try:
1013 | statement, paramName, val = arg.parsed.raw.split(None, 2)
1014 | val = val.strip()
1015 | paramName = paramName.strip().lower()
1016 | if paramName not in self.settable:
1017 | hits = [p for p in self.settable if p.startswith(paramName)]
1018 | if len(hits) == 1:
1019 | paramName = hits[0]
1020 | else:
1021 | return self.do_show(paramName)
1022 | currentVal = getattr(self, paramName)
1023 | if (val[0] == val[-1]) and val[0] in ("'", '"'):
1024 | val = val[1:-1]
1025 | else:
1026 | val = cast(currentVal, val)
1027 | setattr(self, paramName, val)
1028 | self.stdout.write('%s - was: %s\nnow: %s\n' % (paramName, currentVal, val))
1029 | if currentVal != val:
1030 | try:
1031 | onchange_hook = getattr(self, '_onchange_%s' % paramName)
1032 | onchange_hook(old=currentVal, new=val)
1033 | except AttributeError:
1034 | pass
1035 | except (ValueError, AttributeError, NotSettableError) as e:
1036 | self.do_show(arg)
1037 |
1038 | def do_pause(self, arg):
1039 | 'Displays the specified text then waits for the user to press RETURN.'
1040 | raw_input(arg + '\n')
1041 |
1042 | def do_shell(self, arg):
1043 | 'execute a command as if at the OS prompt.'
1044 | os.system(arg)
1045 |
1046 | def do_py(self, arg):
1047 | '''
1048 | py : Executes a Python command.
1049 | py: Enters interactive Python mode.
1050 | End with ``Ctrl-D`` (Unix) / ``Ctrl-Z`` (Windows), ``quit()``, '`exit()``.
1051 | Non-python commands can be issued with ``cmd("your command")``.
1052 | Run python code from external files with ``run("filename.py")``
1053 | '''
1054 | self.pystate['self'] = self
1055 | arg = arg.parsed.raw[2:].strip()
1056 | localvars = (self.locals_in_py and self.pystate) or {}
1057 | interp = InteractiveConsole(locals=localvars)
1058 | interp.runcode('import sys, os;sys.path.insert(0, os.getcwd())')
1059 | if arg.strip():
1060 | interp.runcode(arg)
1061 | else:
1062 | def quit():
1063 | raise EmbeddedConsoleExit
1064 | def onecmd_plus_hooks(arg):
1065 | return self.onecmd_plus_hooks(arg + '\n')
1066 | def run(arg):
1067 | try:
1068 | file = open(arg)
1069 | interp.runcode(file.read())
1070 | file.close()
1071 | except IOError as e:
1072 | self.perror(e)
1073 | self.pystate['quit'] = quit
1074 | self.pystate['exit'] = quit
1075 | self.pystate['cmd'] = onecmd_plus_hooks
1076 | self.pystate['run'] = run
1077 | try:
1078 | cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
1079 | keepstate = Statekeeper(sys, ('stdin','stdout'))
1080 | sys.stdout = self.stdout
1081 | sys.stdin = self.stdin
1082 | interp.interact(banner= "Python %s on %s\n%s\n(%s)\n%s" %
1083 | (sys.version, sys.platform, cprt, self.__class__.__name__, self.do_py.__doc__))
1084 | except EmbeddedConsoleExit:
1085 | pass
1086 | keepstate.restore()
1087 |
1088 | @options([make_option('-s', '--script', action="store_true", help="Script format; no separation lines"),
1089 | ], arg_desc = '(limit on which commands to include)')
1090 | def do_history(self, arg, opts):
1091 | """history [arg]: lists past commands issued
1092 |
1093 | | no arg: list all
1094 | | arg is integer: list one history item, by index
1095 | | arg is string: string search
1096 | | arg is /enclosed in forward-slashes/: regular expression search
1097 | """
1098 | if arg:
1099 | history = self.history.get(arg)
1100 | else:
1101 | history = self.history
1102 | for hi in history:
1103 | if opts.script:
1104 | self.poutput(hi)
1105 | else:
1106 | self.stdout.write(hi.pr())
1107 | def last_matching(self, arg):
1108 | try:
1109 | if arg:
1110 | return self.history.get(arg)[-1]
1111 | else:
1112 | return self.history[-1]
1113 | except IndexError:
1114 | return None
1115 | def do_list(self, arg):
1116 | """list [arg]: lists last command issued
1117 |
1118 | no arg -> list most recent command
1119 | arg is integer -> list one history item, by index
1120 | a..b, a:b, a:, ..b -> list spans from a (or start) to b (or end)
1121 | arg is string -> list all commands matching string search
1122 | arg is /enclosed in forward-slashes/ -> regular expression search
1123 | """
1124 | try:
1125 | history = self.history.span(arg or '-1')
1126 | except IndexError:
1127 | history = self.history.search(arg)
1128 | for hi in history:
1129 | self.poutput(hi.pr())
1130 |
1131 | do_hi = do_history
1132 | do_l = do_list
1133 | do_li = do_list
1134 |
1135 | def do_ed(self, arg):
1136 | """ed: edit most recent command in text editor
1137 | ed [N]: edit numbered command from history
1138 | ed [filename]: edit specified file name
1139 |
1140 | commands are run after editor is closed.
1141 | "set edit (program-name)" or set EDITOR environment variable
1142 | to control which editing program is used."""
1143 | if not self.editor:
1144 | raise EnvironmentError("Please use 'set editor' to specify your text editing program of choice.")
1145 | filename = self.default_file_name
1146 | if arg:
1147 | try:
1148 | buffer = self.last_matching(int(arg))
1149 | except ValueError:
1150 | filename = arg
1151 | buffer = ''
1152 | else:
1153 | buffer = self.history[-1]
1154 |
1155 | if buffer:
1156 | f = open(os.path.expanduser(filename), 'w')
1157 | f.write(buffer or '')
1158 | f.close()
1159 |
1160 | os.system('%s %s' % (self.editor, filename))
1161 | self.do__load(filename)
1162 | do_edit = do_ed
1163 |
1164 | saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums)^'*')("idx") +
1165 | pyparsing.Optional(pyparsing.Word(legalChars + '/\\'))("fname") +
1166 | pyparsing.stringEnd)
1167 | def do_save(self, arg):
1168 | """`save [N] [filename.ext]`
1169 |
1170 | Saves command from history to file.
1171 |
1172 | | N => Number of command (from history), or `*`;
1173 | | most recent command if omitted"""
1174 |
1175 | try:
1176 | args = self.saveparser.parseString(arg)
1177 | except pyparsing.ParseException:
1178 | self.perror('Could not understand save target %s' % arg)
1179 | raise SyntaxError(self.do_save.__doc__)
1180 | fname = args.fname or self.default_file_name
1181 | if args.idx == '*':
1182 | saveme = '\n\n'.join(self.history[:])
1183 | elif args.idx:
1184 | saveme = self.history[int(args.idx)-1]
1185 | else:
1186 | saveme = self.history[-1]
1187 | try:
1188 | f = open(os.path.expanduser(fname), 'w')
1189 | f.write(saveme)
1190 | f.close()
1191 | self.pfeedback('Saved to %s' % (fname))
1192 | except Exception as e:
1193 | self.perror('Error saving %s' % (fname))
1194 | raise
1195 |
1196 | def read_file_or_url(self, fname):
1197 | # TODO: not working on localhost
1198 | if isinstance(fname, file):
1199 | result = open(fname, 'r')
1200 | else:
1201 | match = self.urlre.match(fname)
1202 | if match:
1203 | result = urllib.urlopen(match.group(1))
1204 | else:
1205 | fname = os.path.expanduser(fname)
1206 | try:
1207 | result = open(os.path.expanduser(fname), 'r')
1208 | except IOError:
1209 | result = open('%s.%s' % (os.path.expanduser(fname),
1210 | self.defaultExtension), 'r')
1211 | return result
1212 |
1213 | def do__relative_load(self, arg=None):
1214 | '''
1215 | Runs commands in script at file or URL; if this is called from within an
1216 | already-running script, the filename will be interpreted relative to the
1217 | already-running script's directory.'''
1218 | if arg:
1219 | arg = arg.split(None, 1)
1220 | targetname, args = arg[0], (arg[1:] or [''])[0]
1221 | targetname = os.path.join(self.current_script_dir or '', targetname)
1222 | self.do__load('%s %s' % (targetname, args))
1223 |
1224 | urlre = re.compile('(https?://[-\\w\\./]+)')
1225 | def do_load(self, arg=None):
1226 | """Runs script of command(s) from a file or URL."""
1227 | if arg is None:
1228 | targetname = self.default_file_name
1229 | else:
1230 | arg = arg.split(None, 1)
1231 | targetname, args = arg[0], (arg[1:] or [''])[0].strip()
1232 | try:
1233 | target = self.read_file_or_url(targetname)
1234 | except IOError as e:
1235 | self.perror('Problem accessing script from %s: \n%s' % (targetname, e))
1236 | return
1237 | keepstate = Statekeeper(self, ('stdin','use_rawinput','prompt',
1238 | 'continuation_prompt','current_script_dir'))
1239 | self.stdin = target
1240 | self.use_rawinput = False
1241 | self.prompt = self.continuation_prompt = ''
1242 | self.current_script_dir = os.path.split(targetname)[0]
1243 | stop = self._cmdloop()
1244 | self.stdin.close()
1245 | keepstate.restore()
1246 | self.lastcmd = ''
1247 | return stop and (stop != self._STOP_SCRIPT_NO_EXIT)
1248 | do__load = do_load # avoid an unfortunate legacy use of do_load from sqlpython
1249 |
1250 | def do_run(self, arg):
1251 | """run [arg]: re-runs an earlier command
1252 |
1253 | no arg -> run most recent command
1254 | arg is integer -> run one history item, by index
1255 | arg is string -> run most recent command by string search
1256 | arg is /enclosed in forward-slashes/ -> run most recent by regex
1257 | """
1258 | 'run [N]: runs the SQL that was run N commands ago'
1259 | runme = self.last_matching(arg)
1260 | self.pfeedback(runme)
1261 | if runme:
1262 | stop = self.onecmd_plus_hooks(runme)
1263 | do_r = do_run
1264 |
1265 | def fileimport(self, statement, source):
1266 | try:
1267 | f = open(os.path.expanduser(source))
1268 | except IOError:
1269 | self.stdout.write("Couldn't read from file %s\n" % source)
1270 | return ''
1271 | data = f.read()
1272 | f.close()
1273 | return data
1274 |
1275 | def runTranscriptTests(self, callargs):
1276 | class TestMyAppCase(Cmd2TestCase):
1277 | CmdApp = self.__class__
1278 | self.__class__.testfiles = callargs
1279 | sys.argv = [sys.argv[0]] # the --test argument upsets unittest.main()
1280 | testcase = TestMyAppCase()
1281 | runner = unittest.TextTestRunner()
1282 | result = runner.run(testcase)
1283 | result.printErrors()
1284 |
1285 | def run_commands_at_invocation(self, callargs):
1286 | for initial_command in callargs:
1287 | if self.onecmd_plus_hooks(initial_command + '\n'):
1288 | return self._STOP_AND_EXIT
1289 |
1290 | def cmdloop(self):
1291 | parser = optparse.OptionParser()
1292 | parser.add_option('-t', '--test', dest='test',
1293 | action="store_true",
1294 | help='Test against transcript(s) in FILE (wildcards OK)')
1295 | (callopts, callargs) = parser.parse_args()
1296 | if callopts.test:
1297 | self.runTranscriptTests(callargs)
1298 | else:
1299 | if not self.run_commands_at_invocation(callargs):
1300 | self._cmdloop()
1301 |
1302 | class HistoryItem(str):
1303 | listformat = '-------------------------[%d]\n%s\n'
1304 | def __init__(self, instr):
1305 | str.__init__(self)
1306 | self.lowercase = self.lower()
1307 | self.idx = None
1308 | def pr(self):
1309 | return self.listformat % (self.idx, str(self))
1310 |
1311 | class History(list):
1312 | '''A list of HistoryItems that knows how to respond to user requests.
1313 | >>> h = History([HistoryItem('first'), HistoryItem('second'), HistoryItem('third'), HistoryItem('fourth')])
1314 | >>> h.span('-2..')
1315 | ['third', 'fourth']
1316 | >>> h.span('2..3')
1317 | ['second', 'third']
1318 | >>> h.span('3')
1319 | ['third']
1320 | >>> h.span(':')
1321 | ['first', 'second', 'third', 'fourth']
1322 | >>> h.span('2..')
1323 | ['second', 'third', 'fourth']
1324 | >>> h.span('-1')
1325 | ['fourth']
1326 | >>> h.span('-2..-3')
1327 | ['third', 'second']
1328 | >>> h.search('o')
1329 | ['second', 'fourth']
1330 | >>> h.search('/IR/')
1331 | ['first', 'third']
1332 | '''
1333 | def zero_based_index(self, onebased):
1334 | result = onebased
1335 | if result > 0:
1336 | result -= 1
1337 | return result
1338 | def to_index(self, raw):
1339 | if raw:
1340 | result = self.zero_based_index(int(raw))
1341 | else:
1342 | result = None
1343 | return result
1344 | def search(self, target):
1345 | target = target.strip()
1346 | if target[0] == target[-1] == '/' and len(target) > 1:
1347 | target = target[1:-1]
1348 | else:
1349 | target = re.escape(target)
1350 | pattern = re.compile(target, re.IGNORECASE)
1351 | return [s for s in self if pattern.search(s)]
1352 | spanpattern = re.compile(r'^\s*(?P\-?\d+)?\s*(?P:|(\.{2,}))?\s*(?P\-?\d+)?\s*$')
1353 | def span(self, raw):
1354 | if raw.lower() in ('*', '-', 'all'):
1355 | raw = ':'
1356 | results = self.spanpattern.search(raw)
1357 | if not results:
1358 | raise IndexError
1359 | if not results.group('separator'):
1360 | return [self[self.to_index(results.group('start'))]]
1361 | start = self.to_index(results.group('start'))
1362 | end = self.to_index(results.group('end'))
1363 | reverse = False
1364 | if end is not None:
1365 | if end < start:
1366 | (start, end) = (end, start)
1367 | reverse = True
1368 | end += 1
1369 | result = self[start:end]
1370 | if reverse:
1371 | result.reverse()
1372 | return result
1373 |
1374 | rangePattern = re.compile(r'^\s*(?P[\d]+)?\s*\-\s*(?P[\d]+)?\s*$')
1375 | def append(self, new):
1376 | new = HistoryItem(new)
1377 | list.append(self, new)
1378 | new.idx = len(self)
1379 | def extend(self, new):
1380 | for n in new:
1381 | self.append(n)
1382 |
1383 | def get(self, getme=None, fromEnd=False):
1384 | if not getme:
1385 | return self
1386 | try:
1387 | getme = int(getme)
1388 | if getme < 0:
1389 | return self[:(-1 * getme)]
1390 | else:
1391 | return [self[getme-1]]
1392 | except IndexError:
1393 | return []
1394 | except ValueError:
1395 | rangeResult = self.rangePattern.search(getme)
1396 | if rangeResult:
1397 | start = rangeResult.group('start') or None
1398 | end = rangeResult.group('start') or None
1399 | if start:
1400 | start = int(start) - 1
1401 | if end:
1402 | end = int(end)
1403 | return self[start:end]
1404 |
1405 | getme = getme.strip()
1406 |
1407 | if getme.startswith(r'/') and getme.endswith(r'/'):
1408 | finder = re.compile(getme[1:-1], re.DOTALL | re.MULTILINE | re.IGNORECASE)
1409 | def isin(hi):
1410 | return finder.search(hi)
1411 | else:
1412 | def isin(hi):
1413 | return (getme.lower() in hi.lowercase)
1414 | return [itm for itm in self if isin(itm)]
1415 |
1416 | class NotSettableError(Exception):
1417 | pass
1418 |
1419 | def cast(current, new):
1420 | """Tries to force a new value into the same type as the current."""
1421 | typ = type(current)
1422 | if typ == bool:
1423 | try:
1424 | return bool(int(new))
1425 | except (ValueError, TypeError):
1426 | pass
1427 | try:
1428 | new = new.lower()
1429 | except:
1430 | pass
1431 | if (new=='on') or (new[0] in ('y','t')):
1432 | return True
1433 | if (new=='off') or (new[0] in ('n','f')):
1434 | return False
1435 | else:
1436 | try:
1437 | return typ(new)
1438 | except:
1439 | pass
1440 | print ("Problem setting parameter (now %s) to %s; incorrect type?" % (current, new))
1441 | return current
1442 |
1443 | class Statekeeper(object):
1444 | def __init__(self, obj, attribs):
1445 | self.obj = obj
1446 | self.attribs = attribs
1447 | if self.obj:
1448 | self.save()
1449 | def save(self):
1450 | for attrib in self.attribs:
1451 | setattr(self, attrib, getattr(self.obj, attrib))
1452 | def restore(self):
1453 | if self.obj:
1454 | for attrib in self.attribs:
1455 | setattr(self.obj, attrib, getattr(self, attrib))
1456 |
1457 | class Borg(object):
1458 | '''All instances of any Borg subclass will share state.
1459 | from Python Cookbook, 2nd Ed., recipe 6.16'''
1460 | _shared_state = {}
1461 | def __new__(cls, *a, **k):
1462 | obj = object.__new__(cls, *a, **k)
1463 | obj.__dict__ = cls._shared_state
1464 | return obj
1465 |
1466 | class OutputTrap(Borg):
1467 | '''Instantiate an OutputTrap to divert/capture ALL stdout output. For use in unit testing.
1468 | Call `tearDown()` to return to normal output.'''
1469 | def __init__(self):
1470 | self.contents = ''
1471 | self.old_stdout = sys.stdout
1472 | sys.stdout = self
1473 | def write(self, txt):
1474 | self.contents += txt
1475 | def read(self):
1476 | result = self.contents
1477 | self.contents = ''
1478 | return result
1479 | def tearDown(self):
1480 | sys.stdout = self.old_stdout
1481 | self.contents = ''
1482 |
1483 | class Cmd2TestCase(unittest.TestCase):
1484 | '''Subclass this, setting CmdApp, to make a unittest.TestCase class
1485 | that will execute the commands in a transcript file and expect the results shown.
1486 | See example.py'''
1487 | CmdApp = None
1488 | def fetchTranscripts(self):
1489 | self.transcripts = {}
1490 | for fileset in self.CmdApp.testfiles:
1491 | for fname in glob.glob(fileset):
1492 | tfile = open(fname)
1493 | self.transcripts[fname] = iter(tfile.readlines())
1494 | tfile.close()
1495 | if not len(self.transcripts):
1496 | raise StandardError("No test files found - nothing to test.")
1497 | def setUp(self):
1498 | if self.CmdApp:
1499 | self.outputTrap = OutputTrap()
1500 | self.cmdapp = self.CmdApp()
1501 | self.fetchTranscripts()
1502 | def runTest(self): # was testall
1503 | if self.CmdApp:
1504 | its = sorted(self.transcripts.items())
1505 | for (fname, transcript) in its:
1506 | self._test_transcript(fname, transcript)
1507 | regexPattern = pyparsing.QuotedString(quoteChar=r'/', escChar='\\', multiline=True, unquoteResults=True)
1508 | regexPattern.ignore(pyparsing.cStyleComment)
1509 | notRegexPattern = pyparsing.Word(pyparsing.printables)
1510 | notRegexPattern.setParseAction(lambda t: re.escape(t[0]))
1511 | expectationParser = regexPattern | notRegexPattern
1512 | anyWhitespace = re.compile(r'\s', re.DOTALL | re.MULTILINE)
1513 | def _test_transcript(self, fname, transcript):
1514 | lineNum = 0
1515 | finished = False
1516 | line = transcript.next()
1517 | lineNum += 1
1518 | tests_run = 0
1519 | while not finished:
1520 | # Scroll forward to where actual commands begin
1521 | while not line.startswith(self.cmdapp.prompt):
1522 | try:
1523 | line = transcript.next()
1524 | except StopIteration:
1525 | finished = True
1526 | break
1527 | lineNum += 1
1528 | command = [line[len(self.cmdapp.prompt):]]
1529 | line = transcript.next()
1530 | # Read the entirety of a multi-line command
1531 | while line.startswith(self.cmdapp.continuation_prompt):
1532 | command.append(line[len(self.cmdapp.continuation_prompt):])
1533 | try:
1534 | line = transcript.next()
1535 | except StopIteration:
1536 | raise (StopIteration,
1537 | 'Transcript broke off while reading command beginning at line %d with\n%s'
1538 | % (command[0]))
1539 | lineNum += 1
1540 | command = ''.join(command)
1541 | # Send the command into the application and capture the resulting output
1542 | stop = self.cmdapp.onecmd_plus_hooks(command)
1543 | #TODO: should act on ``stop``
1544 | result = self.outputTrap.read()
1545 | # Read the expected result from transcript
1546 | if line.startswith(self.cmdapp.prompt):
1547 | message = '\nFile %s, line %d\nCommand was:\n%r\nExpected: (nothing)\nGot:\n%r\n'%\
1548 | (fname, lineNum, command, result)
1549 | self.assert_(not(result.strip()), message)
1550 | continue
1551 | expected = []
1552 | while not line.startswith(self.cmdapp.prompt):
1553 | expected.append(line)
1554 | try:
1555 | line = transcript.next()
1556 | except StopIteration:
1557 | finished = True
1558 | break
1559 | lineNum += 1
1560 | expected = ''.join(expected)
1561 | # Compare actual result to expected
1562 | message = '\nFile %s, line %d\nCommand was:\n%s\nExpected:\n%s\nGot:\n%s\n'%\
1563 | (fname, lineNum, command, expected, result)
1564 | expected = self.expectationParser.transformString(expected)
1565 | # checking whitespace is a pain - let's skip it
1566 | expected = self.anyWhitespace.sub('', expected)
1567 | result = self.anyWhitespace.sub('', result)
1568 | self.assert_(re.match(expected, result, re.MULTILINE | re.DOTALL), message)
1569 |
1570 | def tearDown(self):
1571 | if self.CmdApp:
1572 | self.outputTrap.tearDown()
1573 |
1574 | if __name__ == '__main__':
1575 | doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE)
1576 |
1577 | '''
1578 | To make your application transcript-testable, replace
1579 |
1580 | ::
1581 |
1582 | app = MyApp()
1583 | app.cmdloop()
1584 |
1585 | with
1586 |
1587 | ::
1588 |
1589 | app = MyApp()
1590 | cmd2.run(app)
1591 |
1592 | Then run a session of your application and paste the entire screen contents
1593 | into a file, ``transcript.test``, and invoke the test like::
1594 |
1595 | python myapp.py --test transcript.test
1596 |
1597 | Wildcards can be used to test against multiple transcript files.
1598 | '''
1599 |
1600 |
1601 |
--------------------------------------------------------------------------------
/lib/db.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # site : beebeeto.com
4 | # team : n0tr00t security
5 |
6 | import os
7 | import sys
8 | import sqlite3 as sq3
9 | import collections as clt
10 |
11 | try:
12 | import simplejson as json
13 | except ImportError:
14 | import json
15 |
16 | from poc import Poc
17 |
18 |
19 | class Database(object):
20 | tables = {
21 | 'poc': [
22 | 'id',
23 | 'name',
24 | 'rank',
25 | 'level',
26 | 'author',
27 | 'create_date',
28 | 'protocol',
29 | 'port',
30 | 'layer4_protocol',
31 | 'app_name',
32 | 'vul_type',
33 | 'tag',
34 | 'desc',
35 | 'batchable',
36 | 'path',
37 | ],
38 | }
39 |
40 | def __init__(self, dbFilePath='./hive.db', pocDir='./pocs'):
41 | self.dbConn = sq3.connect(dbFilePath)
42 | self.pocDir = pocDir
43 | self.cursor = self.dbConn.cursor()
44 |
45 | def updtDbFromPocs(self, pocDir='../pocs/'):
46 | '''
47 | Update local sqlite database according to the
48 | poc_info in the pocs' source code online database.
49 | '''
50 | updatedNum = 0
51 | insertedNum = 0
52 | errNum = 0
53 | errPocs = []
54 | for pocFileName in os.listdir(pocDir):
55 | if pocFileName.startswith('poc') and pocFileName.endswith('py'):
56 | try:
57 | pocInfo = Poc(os.path.join(pocDir, pocFileName),
58 | batchable=False).module.MyPoc.poc_info
59 | status = self.__updtFromPocInfo(pocInfo)
60 | if status == 'inserted':
61 | insertedNum += 1
62 | elif status == 'updated':
63 | updatedNum += 1
64 | except Exception, err:
65 | errNum += 1
66 | errPocs.append('%s:%s...' % (pocFileName, str(err)[:50]))
67 | self.dbConn.commit()
68 | return (insertedNum, updatedNum, errNum, errPocs)
69 |
70 | def __updtFromPocInfo(self, pocInfo):
71 | self.cursor.execute('SELECT * FROM poc WHERE id=?',
72 | (pocInfo['poc']['id'],))
73 | pocPath = os.path.join(self.pocDir,
74 | '%s.py' % pocInfo['poc']['id'].replace('-', '_'))
75 | if not self.cursor.fetchone():
76 | args = [
77 | pocInfo['poc']['id'],
78 | pocInfo['poc']['name'].decode('utf-8', 'ignore'),
79 | None,
80 | None,
81 | pocInfo['poc']['author'].decode('utf-8', 'ignore'),
82 | pocInfo['poc']['create_date'],
83 | pocInfo['protocol']['name'],
84 | '%s' % ','.join([str(i) for i in pocInfo['protocol']['port']]),
85 | '%s' % ','.join(pocInfo['protocol']['layer4_protocol']),
86 | pocInfo['vul']['app_name'].decode('utf-8', 'ignore'),
87 | pocInfo['vul']['type'],
88 | '%s' % ','.join([i.decode('utf-8', 'ignore') for i in pocInfo['vul']['tag']]),
89 | pocInfo['vul']['desc'].decode('utf-8', 'ignore'),
90 | None,
91 | pocPath if os.path.exists(pocPath) else None,
92 | ]
93 | sql = 'INSERT INTO poc VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
94 | self.cursor.execute(sql, args)
95 | return 'inserted'
96 | else:
97 | args = [
98 | pocInfo['poc']['name'].decode('utf-8', 'ignore'),
99 | pocInfo['poc']['author'].decode('utf-8', 'ignore'),
100 | pocInfo['poc']['create_date'],
101 | pocInfo['protocol']['name'],
102 | '%s' % ','.join([str(i) for i in pocInfo['protocol']['port']]),
103 | '%s' % ','.join(pocInfo['protocol']['layer4_protocol']),
104 | pocInfo['vul']['app_name'].decode('utf-8', 'ignore'),
105 | pocInfo['vul']['type'],
106 | '%s' % ','.join([i.decode('utf-8', 'ignore') for i in pocInfo['vul']['tag']]),
107 | pocInfo['vul']['desc'].decode('utf-8', 'ignore'),
108 | pocPath if os.path.exists(pocPath) else None,
109 | pocInfo['poc']['id'],
110 | ]
111 | sql = 'UPDATE poc SET name=?, author=?, create_date=?, '\
112 | 'protocol=?, port=?, layer4_protocol=?, app_name=?, '\
113 | 'vul_type=?, tag=?, desc=?, path=? WHERE id=?'
114 | self.cursor.execute(sql, args)
115 | return 'updated'
116 |
117 | def updtDbFromJson(self, jsonFile):
118 | '''
119 | Update local sqlite database according to the
120 | dumped json file export from beebeeto.com.
121 | '''
122 | updatedNum = 0
123 | insertedNum = 0
124 | errNum = 0
125 | errPocs = []
126 | f = open(jsonFile, 'rbU')
127 | for row in f:
128 | try:
129 | status = self.__updtFromJsonRow(
130 | dictRow=json.loads(row.strip())
131 | )
132 | if status == 'inserted':
133 | insertedNum += 1
134 | elif status == 'updated':
135 | updatedNum += 1
136 | except Exception, err:
137 | errNum += 1
138 | errPocs.append('%s:%s...' % (row.strip(), str(err)[:50]))
139 | self.dbConn.commit()
140 | f.close()
141 | return (insertedNum, updatedNum, errNum, errPocs)
142 |
143 | def __updtFromJsonRow(self, dictRow):
144 | pocId = dictRow.get('id')
145 | if not pocId:
146 | return
147 | pocPath = os.path.join(self.pocDir,
148 | '%s.py' % pocId.replace('-', '_'))
149 | pocFile = open(pocPath, 'wb')
150 | pocFile.write(dictRow.get('source_code').encode('utf-8', 'ignore'))
151 | pocFile.close()
152 | dbMapping = [
153 | ('id', pocId, ),
154 | ('name', dictRow.get('name'), ),
155 | ('rank', dictRow.get('rank'), ) ,
156 | ('level', dictRow.get('level'), ),
157 | ('author', dictRow.get('author'),),
158 | ('create_date', dictRow.get('create_date'), ),
159 | ('protocol', None, ),
160 | ('port', None, ),
161 | ('layer4_protocol', None, ),
162 | ('app_name', dictRow.get('app_name'), ),
163 | ('vul_type', dictRow.get('vul_type'), ),
164 | ('tag', dictRow.get('tag'), ),
165 | ('desc', dictRow.get('desc'), ),
166 | ('batchable', dictRow.get('batchable'), ),
167 | ('path', pocPath if os.path.exists(pocPath) else None, ),
168 | ]
169 | self.cursor.execute('SELECT * FROM poc WHERE id=?',(pocId,))
170 | if not self.cursor.fetchone():
171 | sql = ' '.join([
172 | 'INSERT INTO poc VALUES',
173 | '(%s)' % ','.join('?' * len(dbMapping)),
174 | ])
175 | self.cursor.execute(sql, map(lambda i: i[1], dbMapping))
176 | return 'inserted'
177 | else:
178 | sql1 = 'UPDATE poc SET'
179 | sql2 = ', '.join(['%s=?' % i[0] for i in dbMapping[1:] if i[1] is not None])
180 | sql3 = 'WHERE id=?'
181 | sql = ' '.join([sql1, sql2, sql3])
182 | args = [i[1] for i in dbMapping[1:] if i[1] is not None]
183 | args.append(pocId)
184 | self.cursor.execute(sql, args)
185 | return 'updated'
186 |
187 | def searchStr(self, item):
188 | columns = [
189 | 'name',
190 | 'app_name',
191 | 'tag',
192 | 'desc',
193 | ]
194 | sql = 'SELECT * FROM poc WHERE ' + ' or '.join(
195 | ['LOWER(%s) like "%%%s%%"' % (col, item.lower()) for col in columns]
196 | )
197 | self.cursor.execute(sql)
198 | return self.cursor.fetchall()
199 |
200 | def searchPoc(self, pocId):
201 | sql = 'SELECT * FROM poc WHERE id=?'
202 | self.cursor.execute(sql, (pocId,))
203 | return self.cursor.fetchone()
204 |
205 | def getBatchable(self):
206 | sql = 'SELECT * FROM poc WHERE batchable=1'
207 | self.cursor.execute(sql)
208 | return self.cursor.fetchall()
209 |
210 | def countAll(self):
211 | sql = 'SELECT count(*) FROM poc'
212 | self.cursor.execute(sql)
213 | return self.cursor.fetchone()
214 |
215 |
216 | if __name__ == '__main__':
217 | # testing code
218 | sys.path.append('../')
219 | from SETTINGS import FRAMEWORK_DIR
220 | sys.path.append(FRAMEWORK_DIR)
221 |
222 | from pprint import pprint as pr
223 |
224 | db = Database(dbFilePath='../hive.db',
225 | pocDir='../pocs/')
226 |
227 | #print db.updtDbFromBB2Db(bb2DbFile='../pocdb.json')
228 | #print db.updtDbFromPocs(pocDir='../pocs')
229 |
230 | #pr(db.searchStr(item='discuz'))
231 | #pr(db.countAll())
232 | #pr(db.searchPoc(pocId='poc-2014-0019'))
233 |
234 | #pr(db.getBatchable())
235 | pr(db.updtDbFromJson(jsonFile='../pocdb.json'))
236 |
--------------------------------------------------------------------------------
/lib/exception.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # site : beebeeto.com
4 | # team : n0tr00t security
5 |
6 |
7 | class BeeScanBaseException(Exception):
8 | pass
9 |
10 |
11 | class BeeScanLaunchException(BeeScanBaseException):
12 | pass
13 |
14 |
15 | class LoadDefaultArgsException(BeeScanBaseException):
16 | pass
17 |
--------------------------------------------------------------------------------
/lib/io.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import sys
4 | import platform
5 | import colorama
6 |
7 | msgTypeColor = {
8 | None: ('', ''),
9 | 'info': (colorama.Fore.BLUE, '[*] '),
10 | 'warning': (colorama.Fore.YELLOW, '[!] '),
11 | 'error': (colorama.Fore.RED, '[-] '),
12 | 'ok': (colorama.Fore.GREEN, '[+] '),
13 | }
14 |
15 | def bprint(msgStr, msgType=None, vbCur=1, vbTs=1):
16 | '''
17 | print function with:
18 | 1. diffrent message type with different color:
19 | info - default color
20 | warning - yellow
21 | error - red
22 | ok - green
23 | 2. verbose level control.
24 | args:
25 | msgStr - the message string.
26 | msgType - message type(info/warning/error/ok).
27 | vbCur - current verbose level.
28 | vbTs - verbose threshold to print the message.
29 | '''
30 | if vbCur >= vbTs:
31 | # deprecated, because colorama.init() disables the auto-completion
32 | # of cmd2, although it works very well in platform auto-detection.
33 | #colorama.init(autoreset=True)
34 |
35 | if platform.system().lower() == 'windows':
36 | stream = colorama.AnsiToWin32(sys.stdout)
37 | else:
38 | stream = sys.stdout
39 |
40 | print(msgTypeColor[msgType][0] + \
41 | colorama.Style.BRIGHT + \
42 | msgStr + \
43 | colorama.Fore.RESET + \
44 | colorama.Style.RESET_ALL,
45 | file=stream)
46 |
47 | def bprintPrefix(msgStr, msgType=None, vbCur=1, vbTs=1):
48 | '''
49 | print function with:
50 | 1. diffrent message type with different color:
51 | info - default color
52 | warning - yellow
53 | error - red
54 | ok - green
55 | 2. verbose level control.
56 | args:
57 | msgStr - the message string.
58 | msgType - message type(info/warning/error/ok).
59 | vbCur - current verbose level.
60 | vbTs - verbose threshold to print the message.
61 | '''
62 | if vbCur >= vbTs:
63 | # deprecated, because colorama.init() disables the auto-completion
64 | # of cmd2, although it works very well in platform auto-detection.
65 | #colorama.init(autoreset=True)
66 |
67 | if platform.system().lower() == 'windows':
68 | stream = colorama.AnsiToWin32(sys.stdout)
69 | else:
70 | stream = sys.stdout
71 |
72 | print(msgTypeColor[msgType][0] + \
73 | colorama.Style.BRIGHT + \
74 | msgTypeColor[msgType][1] + \
75 | colorama.Fore.RESET + \
76 | colorama.Style.RESET_ALL,
77 | end='',
78 | file=stream)
79 | print(msgStr,
80 | file=stream)
81 |
82 |
83 | if __name__ == '__main__':
84 | for msgType, color in msgTypeColor.iteritems():
85 | bprint('233333', msgType)
86 |
87 | for msgType, color in msgTypeColor.iteritems():
88 | bprintPrefix('666666', msgType)
89 |
--------------------------------------------------------------------------------
/lib/poc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # site : beebeeto.com
4 | # team : n0tr00t security
5 |
6 | import os
7 | import re
8 | import imp
9 |
10 | from pprint import pprint
11 | from optparse import Option
12 | from cStringIO import StringIO
13 |
14 |
15 | from exception import LoadDefaultArgsException
16 |
17 |
18 | class Poc(object):
19 | def __init__(self, path, batchable=None):
20 | self.path = path
21 | self.batchable = batchable
22 | self.name = os.path.split(path)[1].rstrip('.py')
23 | self.module = imp.load_source(self.name, path)
24 |
25 | def showInfo(self):
26 | '''print poc_info'''
27 | from pprint import pprint
28 | pprint(self.module.MyPoc.poc_info)
29 |
30 | def _getDefaultOpts(self):
31 | # get the code segment of _init_user_parser()
32 | with open(self.path, 'rbU') as f:
33 | code = f.read()
34 | try:
35 | funcStart = code.index('_init_user_parser(self):')
36 | f.seek(0)
37 | f.read(funcStart)
38 | f.readline() # escape line "def _init_user_parser(self):"
39 | userParserCode = ''
40 | nextLine = f.readline()
41 | while not re.findall('(?:^ \S)|(?:^\S\S)', nextLine):
42 | userParserCode += nextLine
43 | nextLine = f.readline()
44 | pass
45 | except ValueError: # no user defined parser
46 | return {}
47 |
48 | # strip whitespace characters
49 | codeStream = StringIO(userParserCode)
50 | options = []
51 | argsStr = ''
52 | for eachLine in codeStream:
53 | if eachLine.strip().startswith('self.user_parser.add_option'):
54 | options.append(argsStr)
55 | argsStr = eachLine.strip()
56 | else:
57 | argsStr += eachLine.strip()
58 | options.append(argsStr)
59 |
60 | # convert string args into variable instance
61 | reStr = r"([dest|default|type|help|action]+)\s?=\s?(.*?)[,|\)]"
62 | pattern = re.compile(reStr)
63 | optDict = {}
64 | for argsStr in options[1:]:
65 | args = pattern.findall(argsStr)
66 | dictStr = '{%s}' % ','.join("'%s':%s" % \
67 | (i[0], i[1]) for i in args if i[0] != 'help')
68 | argsDict = eval(dictStr)
69 | opt = Option('--arbitrary', **argsDict)
70 | optDict.setdefault(argsDict['dest'], opt.default)
71 | return optDict
72 |
73 | def run(self, target, verify=True):
74 | try:
75 | options = self._getDefaultOpts()
76 | except Exception, err:
77 | raise LoadDefaultArgsException(str(err))
78 | options.update({
79 | 'target': self.module.MyPoc.normalize_target(target),
80 | 'verify': verify,
81 | 'verbose': False,
82 | })
83 | args = {
84 | 'options': options,
85 | 'success': None,
86 | 'poc_ret': {},
87 | }
88 | result = {}
89 | if options['verify']:
90 | ret = self.module.MyPoc.verify(args)
91 | else:
92 | ret = self.module.MyPoc.exploit(args)
93 | result.update(ret)
94 | return result
95 |
96 | if __name__ == '__main__':
97 | # testing code
98 | import os
99 | import sys
100 | BASE_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__),
101 | '..',
102 | '..',
103 | '..'))
104 | FRAMEWORK_DIR = os.path.abspath(os.path.join(BASE_DIR,
105 | 'beebeeto-framework'))
106 | sys.path.extend([BASE_DIR, FRAMEWORK_DIR])
107 |
108 | p = Poc('../pocs/poc_2014_0014.py')
109 |
110 | def test_GetDefaultOpts():
111 | print p._getDefaultOpts()
112 |
113 | def test_run():
114 | print p.run(target='www.baidu.com')
115 |
116 | #test_GetDefaultOpts()
117 | test_run()
118 |
--------------------------------------------------------------------------------
/lib/pprint2.py:
--------------------------------------------------------------------------------
1 | # Author: Fred L. Drake, Jr.
2 | # fdrake@acm.org
3 | #
4 | # This is a simple little module I wrote to make life easier. I didn't
5 | # see anything quite like it in the library, though I may have overlooked
6 | # something. I wrote this when I was trying to read some heavily nested
7 | # tuples with fairly non-descriptive content. This is modeled very much
8 | # after Lisp/Scheme - style pretty-printing of lists. If you find it
9 | # useful, thank small children who sleep at night.
10 |
11 | """Support to pretty-print lists, tuples, & dictionaries recursively.
12 |
13 | Very simple, but useful, especially in debugging data structures.
14 |
15 | Classes
16 | -------
17 |
18 | PrettyPrinter()
19 | Handle pretty-printing operations onto a stream using a configured
20 | set of formatting parameters.
21 |
22 | Functions
23 | ---------
24 |
25 | pformat()
26 | Format a Python object into a pretty-printed representation.
27 |
28 | pprint()
29 | Pretty-print a Python object to a stream [default is sys.stdout].
30 |
31 | saferepr()
32 | Generate a 'standard' repr()-like value, but protect against recursive
33 | data structures.
34 |
35 | """
36 |
37 | import sys as _sys
38 | import warnings
39 | import locale
40 |
41 | from cStringIO import StringIO as _StringIO
42 |
43 | __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
44 | "PrettyPrinter"]
45 |
46 | # cache these for faster access:
47 | _commajoin = ", ".join
48 | _id = id
49 | _len = len
50 | _type = type
51 |
52 |
53 | def pprint(object, stream=None, indent=1, width=80, depth=None):
54 | """Pretty-print a Python object to a stream [default is sys.stdout]."""
55 | printer = PrettyPrinter(
56 | stream=stream, indent=indent, width=width, depth=depth)
57 | printer.pprint(object)
58 |
59 | def pformat(object, indent=1, width=80, depth=None):
60 | """Format a Python object into a pretty-printed representation."""
61 | return PrettyPrinter(indent=indent, width=width, depth=depth).pformat(object)
62 |
63 | def saferepr(object):
64 | """Version of repr() which can handle recursive data structures."""
65 | return _safe_repr(object, {}, None, 0)[0]
66 |
67 | def isreadable(object):
68 | """Determine if saferepr(object) is readable by eval()."""
69 | return _safe_repr(object, {}, None, 0)[1]
70 |
71 | def isrecursive(object):
72 | """Determine if object requires a recursive representation."""
73 | return _safe_repr(object, {}, None, 0)[2]
74 |
75 | def _sorted(iterable):
76 | with warnings.catch_warnings():
77 | if _sys.py3kwarning:
78 | warnings.filterwarnings("ignore", "comparing unequal types "
79 | "not supported", DeprecationWarning)
80 | return sorted(iterable)
81 |
82 | class PrettyPrinter:
83 | def __init__(self, indent=1, width=80, depth=None, stream=None):
84 | """Handle pretty printing operations onto a stream using a set of
85 | configured parameters.
86 |
87 | indent
88 | Number of spaces to indent for each level of nesting.
89 |
90 | width
91 | Attempted maximum number of columns in the output.
92 |
93 | depth
94 | The maximum depth to print out nested structures.
95 |
96 | stream
97 | The desired output stream. If omitted (or false), the standard
98 | output stream available at construction will be used.
99 |
100 | """
101 | indent = int(indent)
102 | width = int(width)
103 | assert indent >= 0, "indent must be >= 0"
104 | assert depth is None or depth > 0, "depth must be > 0"
105 | assert width, "width must be != 0"
106 | self._depth = depth
107 | self._indent_per_level = indent
108 | self._width = width
109 | if stream is not None:
110 | self._stream = stream
111 | else:
112 | self._stream = _sys.stdout
113 |
114 | def pprint(self, object):
115 | self._format(object, self._stream, 0, 0, {}, 0)
116 | self._stream.write("\n")
117 |
118 | def pformat(self, object):
119 | sio = _StringIO()
120 | self._format(object, sio, 0, 0, {}, 0)
121 | return sio.getvalue()
122 |
123 | def isrecursive(self, object):
124 | return self.format(object, {}, 0, 0)[2]
125 |
126 | def isreadable(self, object):
127 | s, readable, recursive = self.format(object, {}, 0, 0)
128 | return readable and not recursive
129 |
130 | def _format(self, object, stream, indent, allowance, context, level):
131 | level = level + 1
132 | objid = _id(object)
133 | if objid in context:
134 | stream.write(_recursion(object))
135 | self._recursive = True
136 | self._readable = False
137 | return
138 | rep = self._repr(object, context, level - 1)
139 | typ = _type(object)
140 | sepLines = _len(rep) > (self._width - 1 - indent - allowance)
141 | write = stream.write
142 |
143 | if self._depth and level > self._depth:
144 | write(rep)
145 | return
146 |
147 | r = getattr(typ, "__repr__", None)
148 | if issubclass(typ, dict) and r is dict.__repr__:
149 | write('{')
150 | if self._indent_per_level > 1:
151 | write((self._indent_per_level - 1) * ' ')
152 | length = _len(object)
153 | if length:
154 | context[objid] = 1
155 | indent = indent + self._indent_per_level
156 | items = _sorted(object.items())
157 | key, ent = items[0]
158 | rep = self._repr(key, context, level)
159 | write(rep)
160 | write(': ')
161 | self._format(ent, stream, indent + _len(rep) + 2,
162 | allowance + 1, context, level)
163 | if length > 1:
164 | for key, ent in items[1:]:
165 | rep = self._repr(key, context, level)
166 | if sepLines:
167 | write(',\n%s%s: ' % (' '*indent, rep))
168 | else:
169 | write(', %s: ' % rep)
170 | self._format(ent, stream, indent + _len(rep) + 2,
171 | allowance + 1, context, level)
172 | indent = indent - self._indent_per_level
173 | del context[objid]
174 | write('}')
175 | return
176 |
177 | if ((issubclass(typ, list) and r is list.__repr__) or
178 | (issubclass(typ, tuple) and r is tuple.__repr__) or
179 | (issubclass(typ, set) and r is set.__repr__) or
180 | (issubclass(typ, frozenset) and r is frozenset.__repr__)
181 | ):
182 | length = _len(object)
183 | if issubclass(typ, list):
184 | write('[')
185 | endchar = ']'
186 | elif issubclass(typ, set):
187 | if not length:
188 | write('set()')
189 | return
190 | write('set([')
191 | endchar = '])'
192 | object = _sorted(object)
193 | indent += 4
194 | elif issubclass(typ, frozenset):
195 | if not length:
196 | write('frozenset()')
197 | return
198 | write('frozenset([')
199 | endchar = '])'
200 | object = _sorted(object)
201 | indent += 10
202 | else:
203 | write('(')
204 | endchar = ')'
205 | if self._indent_per_level > 1 and sepLines:
206 | write((self._indent_per_level - 1) * ' ')
207 | if length:
208 | context[objid] = 1
209 | indent = indent + self._indent_per_level
210 | self._format(object[0], stream, indent, allowance + 1,
211 | context, level)
212 | if length > 1:
213 | for ent in object[1:]:
214 | if sepLines:
215 | write(',\n' + ' '*indent)
216 | else:
217 | write(', ')
218 | self._format(ent, stream, indent,
219 | allowance + 1, context, level)
220 | indent = indent - self._indent_per_level
221 | del context[objid]
222 | if issubclass(typ, tuple) and length == 1:
223 | write(',')
224 | write(endchar)
225 | return
226 |
227 | write(rep)
228 |
229 | def _repr(self, object, context, level):
230 | repr, readable, recursive = self.format(object, context.copy(),
231 | self._depth, level)
232 | if not readable:
233 | self._readable = False
234 | if recursive:
235 | self._recursive = True
236 | return repr
237 |
238 | def format(self, object, context, maxlevels, level):
239 | """Format object for a specific context, returning a string
240 | and flags indicating whether the representation is 'readable'
241 | and whether the object represents a recursive construct.
242 | """
243 | return _safe_repr(object, context, maxlevels, level)
244 |
245 |
246 | # Return triple (repr_string, isreadable, isrecursive).
247 |
248 | def _safe_repr(object, context, maxlevels, level):
249 | typ = _type(object)
250 | if typ is str:
251 | string = object
252 | string = string.replace('\n', '\\n').replace('\r','\\r').replace('\t','\\t')
253 | if 'locale' not in _sys.modules:
254 | return repr(object), True, False
255 | if "'" in object and '"' not in object:
256 | closure = '"'
257 | quotes = {'"': '\\"'}
258 | string = string.replace('"','\\"')
259 | else:
260 | closure = "'"
261 | quotes = {"'": "\\'"}
262 | string = string.replace("'", "\\'")
263 | try:
264 | string.decode('utf8').encode('gbk')
265 | return ("%s%s%s" % (closure, string, closure)), True, False
266 | except:
267 | pass
268 | qget = quotes.get
269 | sio = _StringIO()
270 | write = sio.write
271 | for char in object:
272 | if char.isalpha():
273 | write(char)
274 | else:
275 | write(qget(char, repr(char)[1:-1]))
276 | return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False
277 |
278 | if typ is unicode:
279 | string = object.encode("utf8")
280 | string = string.replace('\n', '\\n').replace('\r','\\r').replace('\t','\\t')
281 | if "'" in object and '"' not in object:
282 | closure = '"'
283 | quotes = {'"': '\\"'}
284 | string = string.replace('"','\\"')
285 | else:
286 | closure = "'"
287 | quotes = {"'": "\\'"}
288 | string = string.replace("'", "\\'")
289 | return ("u%s%s%s" % (closure, string, closure)), True, False
290 |
291 | r = getattr(typ, "__repr__", None)
292 | if issubclass(typ, dict) and r is dict.__repr__:
293 | if not object:
294 | return "{}", True, False
295 | objid = _id(object)
296 | if maxlevels and level >= maxlevels:
297 | return "{...}", False, objid in context
298 | if objid in context:
299 | return _recursion(object), False, True
300 | context[objid] = 1
301 | readable = True
302 | recursive = False
303 | components = []
304 | append = components.append
305 | level += 1
306 | saferepr = _safe_repr
307 | for k, v in _sorted(object.items()):
308 | krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
309 | vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
310 | append("%s: %s" % (krepr, vrepr))
311 | readable = readable and kreadable and vreadable
312 | if krecur or vrecur:
313 | recursive = True
314 | del context[objid]
315 | return "{%s}" % _commajoin(components), readable, recursive
316 |
317 | if (issubclass(typ, list) and r is list.__repr__) or \
318 | (issubclass(typ, tuple) and r is tuple.__repr__):
319 | if issubclass(typ, list):
320 | if not object:
321 | return "[]", True, False
322 | format = "[%s]"
323 | elif _len(object) == 1:
324 | format = "(%s,)"
325 | else:
326 | if not object:
327 | return "()", True, False
328 | format = "(%s)"
329 | objid = _id(object)
330 | if maxlevels and level >= maxlevels:
331 | return format % "...", False, objid in context
332 | if objid in context:
333 | return _recursion(object), False, True
334 | context[objid] = 1
335 | readable = True
336 | recursive = False
337 | components = []
338 | append = components.append
339 | level += 1
340 | for o in object:
341 | orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
342 | append(orepr)
343 | if not oreadable:
344 | readable = False
345 | if orecur:
346 | recursive = True
347 | del context[objid]
348 | return format % _commajoin(components), readable, recursive
349 |
350 | rep = repr(object)
351 | return rep, (rep and not rep.startswith('<')), False
352 |
353 |
354 | def _recursion(object):
355 | return (""
356 | % (_type(object).__name__, _id(object)))
357 |
358 |
359 | def _perfcheck(object=None):
360 | import time
361 | if object is None:
362 | object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
363 | p = PrettyPrinter()
364 | t1 = time.time()
365 | _safe_repr(object, {}, None, 0)
366 | t2 = time.time()
367 | p.pformat(object)
368 | t3 = time.time()
369 | print "_safe_repr:", t2 - t1
370 | print "pformat:", t3 - t2
371 |
372 | if __name__ == "__main__":
373 | _perfcheck()
374 |
--------------------------------------------------------------------------------
/lib/scanner.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # site : www.beebeeto.com
4 | # team : n0tr00t security
5 |
6 | import os
7 | import sys
8 | sys.path.append('../')
9 | import lib
10 | import time
11 | import socket
12 | import random
13 | import string
14 | import platform
15 |
16 | from lib.poc import Poc
17 |
18 | try:
19 | from lib import beecoroutine as bc, beethread as bt
20 | except:
21 | from lib import beethread as bt
22 |
23 |
24 | socket.setdefaulttimeout(10)
25 |
26 |
27 | def TestPlatform():
28 | plat = platform.platform()
29 | if 'windows' in str(plat).lower():
30 | platstr = 'beethread'
31 | else:
32 | platstr = 'beecoroutine'
33 | return platstr
34 |
35 | class Storm(object):
36 | '''scanner scanning a particular target with a list of pocs.'''
37 | def __init__(self, target, listPocPaths, poolModule='beethread',
38 | concurrency=20, verify=True):
39 | '''
40 | target - a target to scan.
41 | listPocPaths - a list containing man pocs' paths, the pocs must be
42 | batchable.
43 | poolModule - can be 'beethread' or 'beecoroutine', meaning using
44 | thread pool or coroutine pool to manage concurrency.
45 | concurrency - how many work to run in concurrency.
46 | verify - run the pocs' verfiy mode or exploit mode.
47 | '''
48 | self.target = target
49 | self.verify = verify
50 | self._pocObjs = self._loadPocs(listPocPaths)
51 | self._poolModule = poolModule
52 | self._workerPool = self._initWorkerPool(poolModule)(concurrency=concurrency)
53 | self.r_num = random.randint(10000, 99999)
54 |
55 | def _view_bar(self, num=1, sum=0, bar_word=':'):
56 | rate = float(num) / float(sum)
57 | rate_num = int(rate * 100)
58 | str_lsit = ['-', '|', '/', '\\']
59 | print '\r[%s] Scanning... (%d%%)' % (random.choice(str_lsit), rate_num),
60 | sys.stdout.flush()
61 |
62 | def _loadPocs(self, listPocPaths):
63 | pocObjs = []
64 | for pocPath in listPocPaths:
65 | pocObjs.append(Poc(path=pocPath))
66 | return pocObjs
67 |
68 | def _initWorkerPool(self, poolModule='beethread'):
69 | if poolModule is 'beethread':
70 | return bt.WorkerPool
71 | elif poolModule is 'beecoroutine':
72 | return bc.WorkerPool
73 |
74 | def _runPoc(self, pocName, pocObj, verbose=True):
75 | if verbose:
76 | tmp_schedule = open('./tmp/t_%d.txt' % self.r_num, 'a+')
77 | tmp_schedule.write(pocName+'\n')
78 | tmp_count = len(open('./tmp/t_%d.txt' % self.r_num ,'rU').readlines())
79 | self._view_bar(num=tmp_count, sum=len(self._pocObjs))
80 | tmp_schedule.close()
81 | return pocObj.run(target=self.target, verify=self.verify)
82 |
83 | def scan(self, timeout=None, verbose=True):
84 | print
85 | self._workerPool.work(iterJobFuncArgs=[[pocObj.name, pocObj, True] \
86 | for pocObj in self._pocObjs],
87 | jobFunc=self._runPoc,
88 | timeout=timeout)
89 | try:
90 | os.remove('./tmp/t_%d.txt' % self.r_num)
91 | except Exception, err:
92 | pass
93 | return self._workerPool.results
94 |
95 |
96 | class Hunter(object):
97 | '''scanner scanning a list of targets with a particular poc.'''
98 | def __init__(self, iterTarget, pocPath, poolModule='beethread',
99 | concurrency=20, verify=True):
100 | self.iterTarget = iterTarget
101 | self.verify = verify
102 | self._pocObj = Poc(path=pocPath)
103 | self._poolModule = poolModule
104 | self._workerPool = self._initWorkerPool(poolModule)(concurrency=concurrency)
105 |
106 | def _initWorkerPool(self, poolModule='beethread'):
107 | if poolModule is 'beethread':
108 | return bt.WorkerPool
109 | elif poolModule is 'beecoroutine':
110 | return bc.WorkerPool
111 |
112 | def _runPoc(self, target):
113 | # TODO: need to normalize target in Poc
114 | target = target.strip()
115 | return self._pocObj.run(target=target, verify=self.verify)
116 |
117 | def scan(self, timeout=None):
118 | self._workerPool.work(iterJobFuncArgs=self.iterTarget,
119 | jobFunc=self._runPoc,
120 | timeout=timeout)
121 | return self._workerPool.results
122 |
123 |
124 |
125 | if __name__ == '__main__':
126 | from pprint import pprint
127 |
128 | # testing Storm
129 | '''
130 | pocPaths = [
131 | './pocs/poc_2014_0014.py',
132 | './pocs/poc_2014_0010.py',
133 | ]
134 | s = Storm(listPocPaths=pocPaths, target='baidu.com', poolModule='beecoroutine')
135 | pprint(s.scan())
136 | '''
137 |
138 | # testing Hunter
139 | '''
140 | targets = ['baidu.com',
141 | 'https://8.8.8.8']
142 | h = Hunter(iterTarget=targets,
143 | pocPath='../pocs/poc_2014_0011.py',
144 | poolModule='beethread')
145 | pprint(h.scan())
146 | '''
147 |
--------------------------------------------------------------------------------
/menu.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # site : www.beebeeto.com
4 | # team : n0tr00t security
5 |
6 | import os
7 | import sys
8 | import optparse
9 | import pyparsing
10 | import pprint as pr
11 | import textwrap as tw
12 | import traceback as tb
13 |
14 | from lib.scanner import Storm, Hunter, TestPlatform
15 | from prettytable import PrettyTable
16 | from lib.io import bprint, bprintPrefix
17 | from lib import db, poc, cmd2, banners, pprint2
18 | from SETTINGS import BEESCAN_DIR, POC_DIR, VERSION
19 |
20 |
21 | # decorater to record last command return value
22 | def recordCmdRet(valName):
23 | def _recordCmdRet(func):
24 | def __recordCmdRet(self, *args, **kwargs):
25 | ret = func(self, *args, **kwargs)
26 | setattr(self, valName, ret)
27 | return
28 | return __recordCmdRet
29 | return _recordCmdRet
30 |
31 |
32 |
33 | class BaseMenu(cmd2.Cmd):
34 | default_to_shell = True
35 | timing = False
36 | nonWhiteMsg = 'Please enter a non white space string.'
37 |
38 | def __init__(self):
39 | super(BaseMenu, self).__init__()
40 | self.database = db.Database(dbFilePath='./hive.db')
41 | self.retLastSearch = None # the result of last search
42 |
43 | def postloop(self):
44 | self.database.dbConn.close()
45 |
46 | def postcmd(self, stop, line):
47 | return stop
48 |
49 | def do_status(self, arg):
50 | '''print status information.'''
51 | bprintPrefix('BeeHive Version: %s' % VERSION, msgType='ok')
52 | bprintPrefix('Exploits & PoCs: %d\n' % self.database.countAll()[0], 'ok')
53 |
54 | @recordCmdRet(valName='retLastSearch')
55 | def do_search(self, arg):
56 | # the func doc below cannot be automatically used as help doc
57 | # because this func is wrapped by a decorator.
58 | if not arg.strip():
59 | bprintPrefix(self.nonWhiteMsg, 'warning')
60 | return
61 | try:
62 | results = self.database.searchStr(arg.strip())
63 | except Exception, err:
64 | print '[-] ',
65 | print err
66 | return
67 | res_tb = PrettyTable(['Name', 'Datetime', 'Level',
68 | 'Author', 'Batch', 'Pid',])
69 | res_tb.align['Name'] = 'l'
70 | for r in results:
71 | res_tb.add_row(((r[1][:35]+'...').strip(),
72 | r[5][:10],r[3],r[4][:10],
73 | r[-2],r[0][4:]))
74 | print res_tb.get_string(sortby='Pid', reversesort=False)
75 | return results
76 |
77 | def help_search(self):
78 | print '''[*] Search for pocs/exps.'''
79 |
80 | def do_run(self, arg):
81 | bprintPrefix("Can't run this command under the root menu.", 'error')
82 | return
83 |
84 | def help_run(self):
85 | print '''[-] UNKOWN'''
86 |
87 | @cmd2.options([cmd2.make_option('-m', '--mode', action='store', help='Update database. (json / pocs)'),])
88 | def do_updatedb(self, arg, opts=None):
89 | ''''''
90 | if opts.mode == 'pocs':
91 | try:
92 | num_insert, num_all, num_err, err_list = self.database.updtDbFromPocs(pocDir=POC_DIR)
93 | print '[*] Scan local mode\n%s\nTotal: %s' % ('--'*10, num_all)
94 | except Exception, err:
95 | bprintPrefix(err, 'error')
96 | elif opts.mode == 'json':
97 | try:
98 | num_insert, num_all, num_err, err_list = self.database.updtDbFromJson('./pocdb.json')
99 | print '[*] JSON import mode\n%s\nTotal: %s' % ('--'*10, num_all)
100 | except Exception, err:
101 | bprintPrefix(err, 'error')
102 | else:
103 | bprintPrefix('WTF!?', 'warning')
104 | return
105 | bprint('Insert number: %s' % num_insert, 'ok')
106 | bprint('Error number: %s' % num_err, 'error')
107 | for i in err_list:
108 | print ' %s' % i
109 |
110 | def do_showloaded(self, arg):
111 | '''[*] Show current loaded poc(s)'''
112 | if hasattr(self, 'loadedPocs') and self.loadedPocs:
113 | if isinstance(self, (ShooterMenu, HunterMenu)):
114 | bprintPrefix('loaded poc: %s' % \
115 | self.loadedPocs.poc_info.get('poc').get('id'), 'ok')
116 | elif isinstance(self, StormMenu):
117 | bprintPrefix('loaded pocs: ', 'ok')
118 | for pocPath in self.loadedPocs:
119 | print ' %s' % os.path.basename(pocPath)
120 | else:
121 | bprint('[-] no poc has been loaded.', 'error')
122 |
123 | def do_lastret(self, arg):
124 | '''[*] Show the result of last scan.'''
125 | if hasattr(self, 'retLastScan') and self.retLastScan:
126 | try:
127 | print
128 | res_tb, ret = self.retLastScan
129 | print res_tb.get_string(sortby='Status', reversesort=False)
130 | print
131 | except Exception, e:
132 | bprintPrefix('%s\n'%str(e), 'warning')
133 | else:
134 | bprintPrefix('No scan result.\n', 'warning')
135 |
136 | def do_export(self, arg):
137 | '''[*] Save the result as a file.'''
138 | if not arg.strip():
139 | bprintPrefix(self.nonWhiteMsg, 'error')
140 | return
141 | if hasattr(self, 'retLastScan') and self.retLastScan:
142 | try:
143 | res_tb, ret = self.retLastScan
144 | _output = open(arg.strip(), 'a+')
145 | _output.write(str(ret))
146 | _output.close()
147 | bprintPrefix('Write file success: %s' % arg.strip(), 'ok')
148 | except Exception, e:
149 | bprintPrefix('%s\n'%str(e), 'warning')
150 | else:
151 | bprintPrefix('No scan result.\n', 'warning')
152 |
153 | def do_info(self, arg):
154 | '''[*] View code information and usage.'''
155 | if not arg.strip():
156 | bprintPrefix(self.nonWhiteMsg, 'error')
157 | return
158 | if not arg.strip().startswith('poc'):
159 | pocName = 'poc-' + arg.strip()
160 | if pocName.strip()[8] != '-':
161 | pocName = 'poc-' + pocName[-8:-4] + '-' + pocName[-4:]
162 | else:
163 | pocName = arg.strip()
164 | pocInfo = self.database.searchPoc(
165 | pocId=pocName.strip().replace('_', '-'))
166 | if pocInfo is None:
167 | bprintPrefix('Cannot find poc %s in database.' % arg, 'error')
168 | return
169 | pocId, name, rank, level, author, createDate, protocol, port, \
170 | layer4Protocol, appName, vulType, desc, tag, batchable, \
171 | path = pocInfo
172 | if not path or not os.path.exists(path):
173 | bprintPrefix('Poc file %s not exists, perhaps you have\'t bought '\
174 | 'it.\n' % path, 'error')
175 | return
176 | try:
177 | p = poc.Poc(path=os.path.join(POC_DIR, '%s.py' % \
178 | pocName.strip().replace('-', '_')),
179 | batchable=batchable)
180 | mp = p.module.MyPoc(run_in_shell=False)
181 | mp._init_parser(do_parse=False)
182 | bprint('%s information:' % path, 'ok')
183 | pprint2.pprint(mp.poc_info)
184 | print
185 | bprint('%s help:' % path, 'ok')
186 | mp.base_parser.print_help()
187 | #return mp
188 | except Exception, err:
189 | bprintPrefix(err, 'error')
190 |
191 | @staticmethod
192 | def extRunPocOpt(option_list, arg_desc="arg"):
193 | '''
194 | Used as a decorator and passed a list of optparse-style options,
195 | alters a cmd2 method to populate its ``opts`` argument from its
196 | raw text argument.
197 |
198 | Example: transform
199 | def do_something(self, arg):
200 |
201 | into
202 | @extPocOpt([make_option('-q', '--quick', action="store_true",
203 | help="Makes things fast")],
204 | "source dest")
205 | def do_something(self, arg, opts):
206 | if opts.quick:
207 | self.fast_button = True
208 | '''
209 |
210 | if not isinstance(option_list, list):
211 | option_list = [option_list]
212 | for opt in option_list:
213 | cmd2.options_defined.append(
214 | pyparsing.Literal(opt.get_opt_string())
215 | )
216 |
217 | def option_setup(func):
218 | optionParser = cmd2.OptionParser()
219 | for opt in option_list:
220 | optionParser.add_option(opt)
221 | optionParser.set_usage("%s [options] %s" % (func.__name__[3:], arg_desc))
222 | optionParser._func = func
223 | def new_func(instance, arg):
224 | try:
225 | for opt in instance.loadedPocs.base_parser._get_all_options():
226 | try:
227 | optionParser._check_conflict(opt)
228 | optionParser.add_option(opt)
229 | except optparse.OptionConflictError as e:
230 | pass
231 | instance.runParser = optionParser
232 | opts, newArgList = optionParser.parse_args(arg.split())
233 | # Must find the remaining args in the original argument list, but
234 | # mustn't include the command itself
235 | #if hasattr(arg, 'parsed') and newArgList[0] == arg.parsed.command:
236 | # newArgList = newArgList[1:]
237 | newArgs = cmd2.remaining_args(arg, newArgList)
238 | if isinstance(arg, cmd2.ParsedString):
239 | arg = arg.with_args_replaced(newArgs)
240 | else:
241 | arg = newArgs
242 | except optparse.OptParseError as e:
243 | print (e)
244 | optionParser.print_help()
245 | return
246 | except AttributeError as e:
247 | bprintPrefix('Please load a poc first.', 'warning')
248 | return
249 | if hasattr(opts, '_exit'):
250 | return None
251 | result = func(instance, arg, opts)
252 | return result
253 | new_func.__doc__ = '%s\n%s' % (func.__doc__, optionParser.format_help())
254 | return new_func
255 | return option_setup
256 |
257 |
258 |
259 | class MainMenu(BaseMenu):
260 | prompt = 'beehive > '
261 |
262 | def preloop(self):
263 | num_count = str(self.database.countAll()[0])
264 | print banners.getBanner()
265 | bprint('%sn0tr00t security team\n' % (' '*20), 'warning')
266 | sys.stdout.write('Beehive Version: ')
267 | bprint(VERSION, 'ok')
268 | sys.stdout.write('Exploits & Pocs: ')
269 | bprint(num_count, 'ok')
270 | sys.stdout.write('Contact: ')
271 | bprint('root@beebeeto.com', 'ok')
272 | sys.stdout.write('Forum: ')
273 | bprint(' http://buzz.beebeeto.com', 'ok')
274 | print
275 |
276 | def do_shooter(self, arg):
277 | '''[*] Go to the shooter(1 poc --> 1 target) sub menu.'''
278 | sm = ShooterMenu()
279 | sm.cmdloop()
280 |
281 | def do_storm(self, arg):
282 | '''[*] Go to the storm(N poc --> 1 target) sub menu.'''
283 | sm = StormMenu()
284 | sm.cmdloop()
285 |
286 | def do_hunter(self, arg):
287 | '''[*] Go to the hunter(1 poc --> N targets) sub menu.'''
288 | hm = HunterMenu()
289 | hm.cmdloop()
290 |
291 |
292 |
293 | class StormMenu(BaseMenu):
294 | prompt = 'beehive.storm > '
295 |
296 | @recordCmdRet(valName='loadedPocs')
297 | def do_loadsearched(self, arg):
298 | if not self.retLastSearch:
299 | bprint('[-] please make a search first.', 'error')
300 | return
301 | batchablePocPaths = []
302 | unbatchablePocPaths = []
303 | for pocInfo in self.retLastSearch:
304 | pocId, name, rank, level, author, createDate, protocol, port, \
305 | layer4Protocol, appName, vulType, desc, tag, batchable, \
306 | path = pocInfo
307 | if batchable:
308 | batchablePocPaths.append(path)
309 | else:
310 | unbatchablePocPaths.append(path)
311 | if unbatchablePocPaths:
312 | bprintPrefix('These pocs in last search results are not batchable:', 'warning')
313 | bprintPrefix('They cannot be loaded in Storm mode, please load them '\
314 | 'singlely in the Shooter mode.', 'warning')
315 | for pocPath in unbatchablePocPaths:
316 | print ' %s' % os.path.basename(pocPath)
317 | if unbatchablePocPaths and batchablePocPaths:
318 | print
319 | if batchablePocPaths:
320 | bprintPrefix('These pocs in last search results are batchable:', 'ok')
321 | bprintPrefix('They are going to be used to load Storm mode scan.', 'ok')
322 | for pocPath in batchablePocPaths:
323 | print ' %s' % os.path.basename(pocPath)
324 | return batchablePocPaths
325 | else:
326 | bprintPrefix('None of the poc in last search result is batchable.', 'warning')
327 | return None
328 |
329 | def help_loadsearched(self):
330 | bprintPrefix('load last searched result(s) to test a target.', 'info')
331 |
332 | @recordCmdRet(valName='retLastScan')
333 | def do_run(self, arg):
334 | if not hasattr(self, 'loadedPocs') or not self.loadedPocs:
335 | bprintPrefix('Please load a poc first.', 'warning')
336 | return
337 | if not arg.strip():
338 | bprintPrefix('Please enter the target.', 'error')
339 | return
340 | s = Storm(target=arg,
341 | listPocPaths=self.loadedPocs,
342 | poolModule=TestPlatform(),
343 | concurrency=20, verify=True)
344 | ret = s.scan()
345 | JOB_UNSTART = 0 # poc not run
346 | JOB_RUNNING = 1
347 | JOB_FINISHED = 2 # poc run ok
348 | JOB_ERROR = -1 # error encountered when run poc
349 | JOB_ABORT = -2 # running poc is abort, viz unfinished
350 | print
351 | bprintPrefix('Scan end, Results:\n', 'ok')
352 | res_tb = PrettyTable(['Vulnerability', 'Pid', 'Status', 'Result',])
353 | res_tb.align['Vulnerability'] = 'l'
354 | for r in ret.values():
355 | pid = r['args'][0].replace('_', '-')
356 | poc_info = self.database.searchPoc(pid)
357 | state = r['state']
358 | if state == JOB_FINISHED:
359 | status = str(r['jobRet']['success'])
360 | result = str(r['jobRet']['poc_ret'])
361 | if status == 'None':
362 | status = 'False'
363 | result = 'N/A'
364 | elif status == 'False':
365 | result = 'Not Vulnerable'
366 | elif state == JOB_ERROR:
367 | status = 'Error'
368 | result = r['exception']
369 | else:
370 | status = 'Error'
371 | res_tb.add_row([poc_info[1][:25]+'...', pid,
372 | status, result[:25]])
373 | print res_tb.get_string(sortby='Status', reversesort=False)
374 | print
375 | return res_tb, ret
376 |
377 | def help_run(self):
378 | bprintPrefix('Run loaded poc(s)', 'info')
379 |
380 | @recordCmdRet(valName='loadedPocs')
381 | def do_loadall(self, arg):
382 | try:
383 | batchablePocs = self.database.getBatchable()
384 | pocPaths = []
385 | [pocPaths.append(i[-1]) for i in batchablePocs]
386 | bprintPrefix('%d batchable pocs (%d total pocs) loaded.' % (
387 | len(pocPaths),
388 | self.database.countAll()[0],
389 | ), 'ok')
390 | return pocPaths
391 | except Exception, err:
392 | print '[-] ',
393 | print err
394 | return
395 |
396 | def help_loadall(self):
397 | bprintPrefix('Load all poc to storm a target.', 'info')
398 |
399 |
400 |
401 | class ShooterMenu(BaseMenu):
402 | prompt = 'beehive.shooter > '
403 |
404 | @recordCmdRet(valName='loadedPocs')
405 | def do_loadpoc(self, arg):
406 | if not arg.strip().startswith('poc'):
407 | pocName = 'poc-' + arg.strip()
408 | if pocName.strip()[8] != '-':
409 | pocName = 'poc-' + pocName[-8:-4] + '-' + pocName[-4:]
410 | else:
411 | pocName = arg.strip()
412 | pocInfo = self.database.searchPoc(
413 | pocId=pocName.strip().replace('_', '-'))
414 | if pocInfo is None:
415 | bprintPrefix('Cannot find poc %s in database.' % arg, 'error')
416 | return
417 | pocId, name, rank, level, author, createDate, protocol, port, \
418 | layer4Protocol, appName, vulType, desc, tag, batchable, \
419 | path = pocInfo
420 | if not path or not os.path.exists(path):
421 | bprintPrefix('Poc file %s not exists, perhaps you have\'t bought '\
422 | 'it.\n' % path, 'error')
423 | return
424 | try:
425 | p = poc.Poc(path=os.path.join(POC_DIR, '%s.py' % \
426 | pocName.strip().replace('-', '_')),
427 | batchable=batchable)
428 | mp = p.module.MyPoc(run_in_shell=False)
429 | mp._init_parser(do_parse=False)
430 | bprintPrefix('load %s success!' % path, 'ok')
431 | return mp
432 | except Exception, err:
433 | bprintPrefix(err, 'error')
434 |
435 | def help_loadpoc(self):
436 | bprintPrefix('Load a poc to test a target.', 'info')
437 |
438 | @recordCmdRet(valName='retLastScan')
439 | @BaseMenu.extRunPocOpt([
440 | cmd2.make_option('-d', '--debug', action="store_true", help="debug mode",)
441 | ])
442 | def do_run(self, arg, opts=None):
443 | if not hasattr(self, 'loadedPocs') or not self.loadedPocs:
444 | bprintPrefix('Please load a poc first.', 'warning')
445 | return
446 | if not opts.target:
447 | bprintPrefix('No target input!\n', 'warning')
448 | self.runParser.print_help()
449 | return
450 | print
451 | ret = self.loadedPocs.run(options=opts.__dict__, debug=opts.debug)
452 | bprintPrefix('%s:\n' % self.loadedPocs.poc_info['poc']['id'], 'info')
453 | # results view
454 | if ret['options']:
455 | print '%starget: %s' % (' '*4, ret['options']['target'])
456 | try:
457 | if ret['exception']:
458 | print '%sexception: %s' % (' '*4, ret['exception'])
459 | except Exception, err:
460 | pass
461 | if ret['success'] == True:
462 | print ' '*4,
463 | bprintPrefix('success: %s' % ret['success'], 'ok')
464 | print ' '*3,
465 | bprintPrefix('poc_ret: %s' % ret['poc_ret'], 'ok')
466 | else:
467 | print '%ssuccess: %s' % (' '*4, ret['success'])
468 | print
469 | return ret
470 |
471 | def help_run(self):
472 | bprintPrefix('Run poc to shoot a target.', 'info')
473 |
474 |
475 |
476 | class HunterMenu(BaseMenu):
477 | prompt = 'beehive.hunter > '
478 |
479 | @recordCmdRet(valName='loadedPocs')
480 | def do_loadpoc(self, arg):
481 | if not arg.strip().startswith('poc'):
482 | pocName = 'poc-' + arg.strip()
483 | if pocName.strip()[8] != '-':
484 | pocName = 'poc-' + pocName[-8:-4] + '-' + pocName[-4:]
485 | else:
486 | pocName = arg.strip()
487 | pocInfo = self.database.searchPoc(
488 | pocId=pocName.strip().replace('_', '-'))
489 | if pocInfo is None:
490 | bprintPrefix('Cannot find poc %s in database.' % arg, 'error')
491 | return
492 | pocId, name, rank, level, author, createDate, protocol, port, \
493 | layer4Protocol, appName, vulType, desc, tag, batchable, \
494 | path = pocInfo
495 | if not path or not os.path.exists(path):
496 | bprintPrefix('Poc file %s not exists, perhaps you have\'t bought '\
497 | 'it.\n' % path, 'error')
498 | return
499 | try:
500 | p = poc.Poc(path=os.path.join(POC_DIR, '%s.py' % \
501 | pocName.strip().replace('-', '_')),
502 | batchable=batchable)
503 | mp = p.module.MyPoc(run_in_shell=False)
504 | mp._init_parser(do_parse=False)
505 | bprintPrefix('load %s success!' % path, 'ok')
506 | return mp
507 | except Exception, err:
508 | bprintPrefix(err, 'error')
509 |
510 | def help_loadpoc(self):
511 | bprintPrefix('Load a poc to test a target.', 'info')
512 |
513 | @recordCmdRet(valName='retLastScan')
514 | @BaseMenu.extRunPocOpt([
515 | cmd2.make_option('-f', '--file', action="store", help="debug mode"),
516 | ])
517 | def do_run(self, arg, opts=None):
518 | if not hasattr(self, 'loadedPocs') or not self.loadedPocs:
519 | bprintPrefix('Please load a poc first.', 'warning')
520 | return
521 | file_alert = 'Need to load a targets file. (domains)'
522 | if not opts.file:
523 | bprintPrefix(file_alert, 'warning')
524 | return
525 | if opts.file:
526 | filename = opts.file
527 | if filename[0] == "'":
528 | filename = filename.strip("'")
529 | elif filename[0] == '"':
530 | filename = filename.strip('"')
531 | try:
532 | f_req = open(filename, 'r')
533 | if os.stat(filename).st_size == 0:
534 | bprintPrefix('File content is empty?', 'warning')
535 | return
536 | except Exception, err:
537 | bprintPrefix(str(err), 'error')
538 | return
539 |
540 | # scan main
541 | pocid = self.loadedPocs.poc_info.get('poc').get('id')
542 | (options, args) = self.loadedPocs.base_parser.parse_args(
543 | arg.strip().split())
544 | h = Hunter(iterTarget=f_req,
545 | pocPath=('./pocs/%s.py' % pocid.replace('-', '_')),
546 | poolModule=TestPlatform())
547 | ret = h.scan()
548 |
549 | # view table
550 | JOB_UNSTART = 0 # poc not run
551 | JOB_RUNNING = 1
552 | JOB_FINISHED = 2 # poc run ok
553 | JOB_ERROR = -1 # error encountered when run poc
554 | JOB_ABORT = -2 # running poc is abort, viz unfinished
555 | print
556 | res_tb = PrettyTable(['Target', 'Status', 'Result',])
557 | res_tb.align['Target'] = 'l'
558 | try:
559 | for r in ret.values():
560 | target = r['args']
561 | pid = r['args'][0].replace('_', '-')
562 | poc_info = self.database.searchPoc(pid)
563 | state = r['state']
564 | if state == JOB_FINISHED:
565 | status = str(r['jobRet']['success'])
566 | result = str(r['jobRet']['poc_ret'])
567 | if status == 'None':
568 | status = 'False'
569 | result = 'N/A'
570 | elif status == 'False':
571 | result = 'Not Vulnerable'
572 | elif state == JOB_ERROR:
573 | status = 'Error'
574 | result = r['exception']
575 | else:
576 | status = 'Error'
577 | res_tb.add_row([target, status, result[:25]])
578 | except Exception, err:
579 | import traceback
580 | traceback.print_exc()
581 | print res_tb.get_string(sortby='Status', reversesort=False)
582 | print
583 | return res_tb, ret
584 |
585 | def help_run(self):
586 | bprintPrefix('Run loaded poc(s)', 'info')
587 |
588 |
589 | if __name__ == '__main__':
590 | mm = MainMenu()
591 | mm.cmdloop()
592 |
--------------------------------------------------------------------------------
/pocs/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n0tr00t/Beehive/24227d00f2db5299ffd426bc662deac8469badf3/pocs/__init__.py
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | gevent
2 | colorama
3 | pyparsing
4 | threadpool
5 | pyreadline
6 | prettytable
7 | requests
8 | redis
9 | paramiko
10 | pymongo
11 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | # site : www.beebeeto.com
4 | # team : n0tr00t security
5 |
6 | import os
7 | import sys
8 | import platform
9 | try:
10 | from lib.io import bprintPrefix
11 | except ImportError as err:
12 | def bprintPrefix(msg, key):
13 | print("%s %s" % (msg, key))
14 |
15 | def TestPlatform():
16 | plat = platform.platform()
17 | platstr = 'other'
18 | if 'windows' in str(plat).lower():
19 | platstr = 'windows'
20 | return plat, platstr
21 |
22 | def Framework_check():
23 | plat, platstr = TestPlatform()
24 | if platstr == 'windows':
25 | framework_path = '%s\\Beebeeto-framework\\' % ('\\'.join(os.getcwd().split('\\')[:-1]))
26 | else:
27 | framework_path = '%s/Beebeeto-framework/' % ('/'.join(os.getcwd().split('/')[:-1]))
28 | if os.path.exists(framework_path) == True:
29 | return True
30 | else:
31 | return False
32 |
33 | def Requirements():
34 | requirements = []
35 | [requirements.append(r.strip()) for r in open('requirements.txt', 'r').readlines()]
36 | return requirements
37 |
38 | def Install(name, pip_proxy=False):
39 | print
40 | pip_proxy_address = 'http://mirrors.aliyun.com/pypi/simple/'
41 | pip_proxy_host = 'mirrors.aliyun.com'
42 | bprintPrefix('%s installing...' % name, 'ok')
43 | if pip_proxy == True:
44 | os.system('pip install %s -i %s --trusted-host %s' % (name, pip_proxy_address, pip_proxy_host))
45 | else:
46 | os.system('pip install %s' % name)
47 |
48 | def Main(pip_proxy):
49 | try:
50 | bprintPrefix('pip version: %s' % (os.popen('pip --version').readlines()[0].strip()), 'info')
51 | except Exception as err:
52 | bprintPrefix(str(err), 'error')
53 | sys.exit()
54 | requirements = Requirements()
55 | plat, platstr = TestPlatform()
56 | if platstr == 'windows':
57 | requirements.remove('gevent')
58 | else:
59 | requirements.append('readline')
60 | bprintPrefix('Platform: %s' % plat, 'info')
61 | if Framework_check() == True:
62 | bprintPrefix('Beebeeto-framework check: ok', 'ok')
63 | else:
64 | bprintPrefix('Beebeeto-framework check: false', 'error')
65 | bprintPrefix('Installing...', 'info')
66 | if platstr == 'windows':
67 | os.system('cd .. & git clone https://github.com/n0tr00t/Beebeeto-framework')
68 | else:
69 | os.system('cd ../ ; git clone https://github.com/n0tr00t/Beebeeto-framework')
70 | [Install(r, pip_proxy) for r in requirements]
71 | bprintPrefix('Finish :)', 'ok')
72 |
73 | if __name__ == '__main__':
74 | pip_proxy= False
75 | if len(sys.argv) > 1 and sys.argv[1] == 'proxy':
76 | pip_proxy = True
77 | Main(pip_proxy)
78 |
--------------------------------------------------------------------------------
/tmp/tmp.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n0tr00t/Beehive/24227d00f2db5299ffd426bc662deac8469badf3/tmp/tmp.txt
--------------------------------------------------------------------------------